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 MAIN
s.
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 Int
s (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.
-
December 6, 2021 at 21:362021.49 Adventing Is On! – Rakudo Weekly News