with Raku

This is my response to the Perl Weekly Challenge #153.

Write a script to compute `Left Factorials`

of `1 to 10`

.
Please refer OEIS A003422 for more information.

1, 2, 4, 10, 34, 154, 874, 5914, 46234, 409114

What *exactly* is the meaning of compute? Let us start with a very silly version:

#! /usr/bin/env raku
say "1, 2, 4, 10, 34, 154, 874, 5914, 46234, 409114";

Running it gives what you would expect:

$ ./left-factorials-silly
1, 2, 4, 10, 34, 154, 874, 5914, 46234, 409114

The OEIS article reveals that the formula for the
`n`

th value is `(0! .. n!).sum`

. The first value in the sequence
is zero, but the challenge skips that one and starts with 1 (which according to OEIS
is the second value). Let us do what the challenge asks for, inititally at least.

#! /usr/bin/env raku
my $lf := gather
{
my $index = 0; # [1]
loop # [2]
{
take (0 .. $index++).map({ [*] (1 .. $_) }).sum; # [3]
}
}
say $lf[^10]].join(", ");

[1] Used to keep track of the current index, as the formulae requires it.

[2] An eternal loop, giving one value for each iteration.

{3] The inside of the «map»
(`({ [*] (1 .. $_) })`

is the faculty function, implemented with the Reduction
Metaoperator `[]`

that (in this case, as the operator is `*`

)
multiplies all the values together. The values are the numbers from 1 up to `$_`

.
The first part (`(0 .. $index++).map(...)`

) gives us all the indices, and maps
them to the corresponding faculty value. (I.e. 3 gives 3!). The final `.sum`

adds them together, giving the current value in the sequence.

See
docs.raku.org/language/operators#Reduction_metaoperators for more
information about the Reduction Metaoperator `[]`

.

Running it:

$ ./left-factorials-loop
1, 2, 4, 10, 34, 154, 874, 5914, 46234, 409114

Note that this will recalculate all the factorial values a lot of times. We can - and indeed should - cache them:

File: left-factorials-loop-cached#! /usr/bin/env raku
my $lf := gather
{
my $index = 0;
my $prev = 0; # [1]
loop
{
take $prev += ( [*] (1 .. $index++) ).sum; # [2]
}
}
say $lf[^10].join(", ");

[1] The previous value in the sequence, used by the next one. This is the cached value.

[2] Get the faculty of the new index, and add that to the previous value.

The result is as expected:

$ ./left-factorials-loop-cached
1, 2, 4, 10, 34, 154, 874, 5914, 46234, 409114

Let us amend this, so that we can get the first (or zero) value of 0, as OEIS would like us to:

File: left-factorials-loop-cached-one#! /usr/bin/env raku
unit sub MAIN (:z(:$zero-based));
my $lf := gather
{
my $index = 0;
my $prev = 0;
take 0 if $zero-based; # [1]
loop
{
take $prev += ( [*] (1 .. $index++) ).sum;
}
}
say $lf[^10].join(", ");

[1] Instead of rewriting the loop, we can just insert the initial zero like this.

Running it gives the expected result:

$ ./left-factorials-loop-cached-one
1, 2, 4, 10, 34, 154, 874, 5914, 46234, 409114
$ ./left-factorials-loop-cached-one -z
0, 1, 2, 4, 10, 34, 154, 874, 5914, 46234

We can actually do this with a proper Raku sequence, which is very compact:

File: left-factorials-seq-zero#! /usr/bin/env raku
unit sub MAIN (:c(:$count) = 10);
my $lf := ( 0, 1, ( * + ([*] 1 .. ++$) ) ... Inf );
# ######## [1] [2] [3] ################# [4] #####
say $lf[^$count].join(", ");

[1] The first value is 0.

[2] The second value is 1.

[3] Then a rule for all the values following
the first two. Start with the previous value in the sequence (the first `*`

),
and add it to the expression after the plus sign. That expression is the faculty
function applied to `$`

, which is an anonymous state variable (that pops
into existence when we use it, and keeps the value between calls).

See
docs.raku.org/syntax/$ for more
information about the anonymous state variable `$`

.

[4] Go on indefinitely.

The result is as expected:

$ ./left-factorials-seq-zero
0, 1, 2, 4, 10, 34, 154, 874, 5914, 46234

The non-zero version is a little bit trickier:

File: left-factorials-seq#! /usr/bin/env raku
unit sub MAIN (:c(:$count) = 10);
my $lf := ( $ = 1, ( * + ([*] 1 .. ++$) ) ... Inf ); # [1]
say $lf[^$count].join(", ");

[1] This time we skip the initial zero in the sequence, but we have to initialise the state variable to 1 (which is also the first value, as it is returned from the assignment), so that it starts with the correct index value.

All is well:

$ ./left-factorials-seq-zero
0, 1, 2, 4, 10, 34, 154, 874, 5914, 46234
$ ./left-factorials-seq-zero -c=12
0, 1, 2, 4, 10, 34, 154, 874, 5914, 46234, 409114, 4037914

You are given an integer,

Write a script to figure out if the given integer is factorion.

A factorion is a natural number that equals the sum of the factorials of its digits.

Example 1:

`$n`

.
Write a script to figure out if the given integer is factorion.

A factorion is a natural number that equals the sum of the factorials of its digits.

Example 1:

Input: $n = 145
Output: 1
Since 1! + 4! + 5! => 1 + 24 + 120 = 145

Example 2:
Input: $n = 123
Output: 0
Since 1! + 2! + 3! => 1 + 2 + 6 <> 123

Let us go for a compact version:

File: factorions#! /usr/bin/env raku
unit sub MAIN (Int $n where $n > 0); # [1]
say + ($n.comb.map({ [*] (1 .. $_) }).sum == $n); # [2]

[1] Ensure a natural number, i.e. a positive integer.

[2] We use `comb`

to get a list
of digits, then `map`

to apply the faculty function (as shown in the first
part of the challenge) on them. Then we add them together (with `sum`

) and
compare the result with the initial number. The result of this comparison is a
Boolean value, so we coerce it to an integer (True => 1, False => 0) with the Numeric
Coercion Prefix Operator `+`

(that has a very impressive (as in *long*)
name, especially compared with the single character of the operator itself).

See
docs.raku.org/routine/+ for
more information about the Numeric Coercion Prefix Operator `+`

.

Running it:

$ ./factorions 145
1
$ ./factorions 123
0

Ok.

Let us have a go at the Factorion Sequence:

File: factorion-seq#! /usr/bin/env raku
unit sub MAIN (:c(:$count) = 10);
my $fs := ( my $i = 1,
{ (++$i).comb.map({ [*] (1 .. $_) }).sum == $i ?? $i !! next } [1]
... Inf
);
say $fs[^$count].join(", ");

[1] Note the `next`

inside the rule (as `map`

is a loop in disguise), instead of the more intuitive `grep`

on the outside.
This is necessary, as `grep`

will coerce whatever it is used on to a list.
Expanding an eternal sequence is not a good idea.

The statement above is wrong, as lists can be lazy. We can use `grep`

here. See Fooled by a Sequence, Twice for details.

Also note the ternary operator `??`

and `!!`

instead of a full blown
`if`

and `else`

.

See
docs.raku.org/routine/grep
for more information about `grep`

.

See
docs.raku.org/language/operators#index-entry-operator_ternary for more
information about the *ternary operator* `??`

/ `!!`

.

Running it:

$ ./factorion-seq
^C

It hangs.

Looking it up (at mathworld.wolfram.com/Factorion.html) tells us that there are only 4 values, so the program happily goes on to infinity searching for the default 10 values. (Well, it tries to. Infinity is not reachable.)

This is the four values:

$ ./factorion-seq -c=4
1, 2, 145, 40585

And that's it.