Download the raw code.

use v6;

# Problem 1 -- Find a way to express an integer as an expression containing four 9s
# Since there's only 64 sequences of 9's with the basic arithmetic operators
# between them, I generate all the sequences.  If there were twelve nines (about
# four million permutations), I might think about using a different approach.

die "Maximum index argument required." unless @*ARGS;
my $max = @*ARGS[0].Int;
die "Maximum index argument must be an integer" if $max ne @*ARGS[0];

my @operators = <+ - * />;
my %operation = @operators Z=> (&infix:<+>, &infix:<->, &infix:<*>, &infix:</>);

# Skip some of the equivalent expressions. There are more, but these are the
# only set of length two, which are going to have the most impact.
my $skip = any(<-+ /*>);

my %valid;

# List auto-flattening makes this more verbose than it should need to be.  While
# I don't think that it's neccessarily a bug, it would be nice to have some way
# to have the for loop not auto-flatten my cross operation.
my $cross := infix:<X>(|([[@operators]] xx 3));
for ^$cross.elems -> $index
    my @ops_str = @($cross[$index]);
    next if any(@ops_str Z~ @ops_str[1 .. *]) ~~ $skip;
    my @ops = %operation{@ops_str};
    my $result = (9, @ops).reduce: -> $so-far, $op { $op($so-far,9) };
    next unless $result == $result.Int && $result > 0 && $result <= $max;
    # RAKUDOBUG: Hash.push was flattening my list no matter how many sets
    # of square brackets I put there.
    %valid{$result} = [] unless %valid.exists($result);
    my $value = [@ops_str];
    %valid{$result} = [@(%valid{$result}), $value];

for 1 .. $max -> $num
    print $num;
    if %valid.exists: $num
        print ":";
        my $first = True;
        for @(%valid{$num}) -> $expr
            print " OR" if !$first;
            $first = False;
            print ' ' ~ ((9 xx $expr.elems Z $expr), 9);
    print "\n";


A brief excerpt from the output demonstrates the problem:

3: 9 + 9 + 9 / 9

No parenthesis, expressions are always evaluated from left to right. That is not only a problem of the output routine, but a deeper one. Without using parentheses, you cannot express 324 = (9 + 9) * (9 + 9) because it doesn't evaluate from left to right. Also, the program starts with printing the 1, ignoring the 0. In total it produces only 20 of the 29 numbers between 0 and 1000.


Consistent layout with helpful comments.

Clarity of intent

The code is dense, but not hard to follow.

Algorithmic efficiency

Reasonable. It is one of the faster solutions, at the price of not finding all expressions. As MJD says, "It's easy to get the wrong answer in O(1)".

Idiomatic use of Perl 6

There's much to like: the use of hash slicing, the X and Z operators and meta operators, .reduce, and the reasonable use of junctions. If you know that $expr holds the operators,

print ' ' ~ ((9 xx $expr.elems Z $expr), 9);

is cute.

Some lines could have been a bit more idiomatic, for example the unwieldly

%valid{$result} = [] unless %valid.exists($result);

Could have been shortened to

%valid{$result} //= [];

Some lines were wasted on validating command-line arguments, which could've been more idiomatically done with MAIN.


Oh yes. Ignoring comments and blank lines, there are 8 lines declarations and initialization, 12 lines of generating the expresions, and another 16 for printing. But then it does less than other solutions, and too little at that.