Download the raw code.
##!/usr/bin/env perl6
sub parens( $s ) {
return $s eq '9' ?? $s !! '(' ~ $s ~ ')';
};
sub operate( $num1, $exp1, $num2, $exp2 ) {
my @result;
push @result, $num1 + $num2;
push @result, parens( $exp1 ) ~ " + " ~ parens( $exp2 );
push @result, $num1 - $num2;
push @result, parens( $exp1 ) ~ " - " ~ parens( $exp2 );
push @result, $num1 * $num2;
push @result, parens( $exp1 ) ~ " * " ~ parens( $exp2 );
if $num2 != 0 {
push @result, $num1 / $num2;
push @result, parens( $exp1 ) ~ " / " ~ parens( $exp2 );
push @result, $num1 % $num2;
push @result, parens( $exp1 ) ~ " % " ~ parens( $exp2 );
}
return @result;
}
sub mate-levels( %numexp1, %numexp2 ) {
my @result;
for %numexp1.kv -> $num1, $exp1 {
for %numexp2.kv -> $num2, $exp2 {
push @result, operate $num1, $exp1, $num2, $exp2;
push @result, operate $num2, $exp2, $num1, $exp1;
}
}
return @result;
}
sub insert-num-exp-pairs( %dst, @new ) {
for @new -> $num, $exp {
# Select the most readable (=shortest) expression.
if not %dst.exists( $num ) or %dst{$num}.chars > $exp.chars {
%dst{$num} = $exp;
}
}
}
sub MAIN ( Int $N ) {
# @all[1] holds numbers and expressions built with 1 nine.
# @all[2..4] will hold numbers and expressions built with 2..4 nines.
my @all = (
{},
{ 9 => "9", -9 => "-9" },
{},
{},
{}
);
insert-num-exp-pairs( @all[2], mate-levels( @all[1], @all[1] ));
insert-num-exp-pairs( @all[3], mate-levels( @all[1], @all[2] ));
insert-num-exp-pairs( @all[4], mate-levels( @all[1], @all[3] ));
insert-num-exp-pairs( @all[4], mate-levels( @all[2], @all[2] ));
for 0 .. $N -> $n {
say $n ~ ( @all[4].exists( $n ) ?? ' = ' ~ @all[4]{$n} !! '' );
};
}
This program produces the correct output, both with rakudo and niecza.
Neatly laid out, The abbreviation exp
stands for expression
throughout
the program. It's a perhaps a bit non-standard (expr
would've been clearer),
but it's consistently used.
insert-num-exp-pairs
populates the first argument, while all other
routines return the result of their computation as normal return values.
That makes it a bit slower to understand the code.
The subroutine operate
contains several lines that only differ in the
operator they use, and thus look like duplicated code. A data-driven
approach might've made the code less repetitive.
The author has written an extra subroutine to avoid unnecessary
parentheses in the output, but then even calls it where it's not needed
(the case where the operator is +
does not need to call parens
for
its arguments).
Most solutions to this task scale the same way, they simply exhaust the space of possible solutions. This one is no different. It could be a bit faster by pruning non-integer intermediate results, but that would not place it in a different big-O category. So no real reason to complain here.
The code uses some nice Perl 6 idioms like pointy blocks, iterating
a list two items at a time, and sub MAIN
.
This code gets to the point fast. The repetition mentioned above give the sense that some of the code could have been DRY-d up a bit. Still not bad.