t1/az5112

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} !! '' );
    };
}

Correctness

This program produces the correct output, both with rakudo and niecza.

Consistency

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.

Clarity of intent

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).

Algorithmic efficiency

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.

Idiomatic use of Perl 6

The code uses some nice Perl 6 idioms like pointy blocks, iterating a list two items at a time, and sub MAIN.

Brevity

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.