Home > Raku > MONKEY-SEE-NO-CROSSPRODUCT

MONKEY-SEE-NO-CROSSPRODUCT

The challenge of the week is screaming: “Nest all the loops!”. I don’t like being yelled at, so I refuse to use any nested for/while/loop. The rules don’t require to put the two sub-challenges into separate files, so I won’t.

#! /usr/bin/env raku

use v6.d;

multi sub MAIN($divisors = 8, $? where $*PROGRAM-NAME.contains('-141-1')) {
    sub has-m-divisors($m, $n) {
        (1..∞).grep({ last if $_ > $n; $n %% $_ ?? (last if $++ ≥ $m; True) !! False })[^∞].elems == $m;
    }
    put (1..∞).grep(&has-m-divisors.assuming($divisors))[^10];
}

multi sub MAIN($m, $n where $*PROGRAM-NAME.contains('-141-2')) {
    use MONKEY-SEE-NO-EVAL;

    sub bind($o, Str $method-name) {
        sub (|c) { $o."$method-name"(|c) }
    }

    sub is-same-order(@l) {
        my @indices = @l.map(bind($m, 'index'));
        @indices ~~ @indices.sort;
    }

    say [$m, $n];
    my @a = $m.comb;
    my $expr = ('@a' xx @a).join(' X, '); # I can haz RakuAST macro plox?
    my @digits = (EVAL $expr)».unique.grep(-> @l { @l < @a && is-same-order(@l) }).sort(*.elems)».join.unique».Int;
    say @digits.grep: * %% $n;
}

multi sub MAIN($? where $*PROGRAM-NAME.contains('-141-2')) {
    for (1234, 2; 768, 4) -> [$m, $n] {
        MAIN($m, $n);
    }
}

To be able to run the 2nd task, ln -s pwc-141-1.raku pwc-141-2.raku is required. Since we can’t have a bare where-clause (yet), we need the optional positional in the MAINs.

Task one basically builds an infinite list of integers that have 8 divisors and then shows the first 10 of those. This is done by iterating to infinity and bailing as early as possible, if the condition of having 8 divisors is net or we get beyond the to be checked value. Since grep wants arity of 1, we use .assuming to please it.

The 2nd task is attacked (appropriate wording, as it took me 3 hours of struggle to get it done) in a similar fashion. First we build all possible combinations of an n-places integer. This would be done with an expression like @a xx @a ... $n where $n is the number of places. I don’t think we got such a construct in Raku so I had to macro it in with EVAL (also, it made a good blog title). We then proceed with eliminating all combinations that are forbidden by the task. First we weed out repetition with ».unique. Next we only take integers that are shorter then the original integer and don’t have the same order. We sort the result list by number of digits to make it pretty. At this point the digits are still represented as a list. We change that by forcing them into a Str via ».join and remove duplicates and turn them into Ints (not strictly necessary). Now we fulfil the last condition of divisibility by $n.

The sub is-same-order was where I spend the most time. And you can tell by how short it is. The idea is simple. We build a list of indices by walking the places of the number we want to check against the original integer. If the indices are all ascending, the order is the same. And we can tell by checking if those are already numerically sorted.

I’m not happy with using bind. This is another spot where Raku won’t allow functional programming to integrate well with OO. I’m gonna ask Santa if he can make &$m.index DWIM.

UPDATE

As perryprog kindly pointed out on IRC, there is sub cross, what makes the EVAL redundant.

my @digits = cross(@a xx +@a)».unique.grep(-> @l { @l < @a && is-same-order(@l) }).sort(*.elems)».join.unique».Int;

However, I think we can all agree that there is no immediate need to surrender a good blog post title.

Categories: Raku