with Raku

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

Write a script to find out all

Additive primes are prime numbers for which the sum of their decimal digits are also primes.

Output:

`Additive Primes <= 100`

.
Additive primes are prime numbers for which the sum of their decimal digits are also primes.

Output:

2, 3, 5, 7, 11, 23, 29, 41, 43, 47, 61, 67, 83, 89

The *highest value* restraint is a new twist. We have been tasked to print a *specifc
number of values* before - and that is easy. This challenge is similar to the
Pernicious Numbers (see Weirdly Pernicious with Raku),
if we just want the sequence:

#! /usr/bin/env raku
unit sub MAIN (Int $length where $length > 0 = 10); # [1]
my $ap := (1..Inf).grep( *.is-prime ).grep( *.comb.sum.is-prime ); # [2]
$ap.head($length).join(", ").say;

[1] No upper limit, but the number of values to print.

[2] The numner itself must be a prime (the first `grep`

), as must the
sum of the digits (the second `grep`

).

Running it:

$ ./additive-primes-seq
2, 3, 5, 7, 11, 23, 29, 41, 43, 47
$ ./additive-primes-seq 19
2, 3, 5, 7, 11, 23, 29, 41, 43, 47, 61, 67, 83, 89, 101, 113, 131, 137, 139
$ ./additive-primes-seq 14
2, 3, 5, 7, 11, 23, 29, 41, 43, 47, 61, 67, 83, 89

Looking good.

Let us have a go at optimizing the expression. Daisy chaining `grep`

twice is silly.

First the original:

> (1..Inf).grep( *.is-prime ).grep( *.comb.sum.is-prime ).head(14)
(2 3 5 7 11 23 29 41 43 47 61 67 83 89)

Then we merge them with `&&`

:

> (1..Inf).grep( *.is-prime && *.comb.sum.is-prime ).head(14)
(2 3 5 7 11 12 14 16 20 21 23 25 29 30)

Oops!

The first part (the left hand side of `&&`

) does not matter, as
we get the exact same result without it:

> (1..Inf).grep( *.comb.sum.is-prime ).head(14)
(2 3 5 7 11 12 14 16 20 21 23 25 29 30)

This one is pretty absurd, as it would seem to imply that the values are primes and not primes - at the same time:

> (1..Inf).grep( *.is-prime.not && *.is-prime ).head(14)
(2 3 5 7 11 13 17 19 23 29 31 37 41 43)

Actually, it is. (Whatever, that is.)

The problem is that `*`

(the first star) and
`*`

(the second star) are not the same thing(y). The Fibonacci Sequence set
up with two of those Whatever stars is illuminating:

my @f = 0, 1, * + * ... *;

The expression must be read from the right, to avoid headache. The rightmost star is the same is Infinity. The next one (to the left) refers to the previous value in the sequence, and the leftmost one refers to the value before that again.

See
docs.raku.org/type/Whatever
for more information about the `Whatever`

class (and star placeholder).

The `grep`

block is supplied with one argument for each iteration (as it
is a loop in disguise). The first star does not have anything to refer to, and for
some reason (that is beyond me) that part is simply ignored.

Using a closure (the `{`

and `}`

brackets) does the trick:

> (1..Inf).grep( { .is-prime && .comb.sum.is-prime } ).head(14)
(2 3 5 7 11 23 29 41 43 47 61 67 83 89)

Then we can have a go at the values <= 100, as specified in the challenge:

File: additive-primes#! /usr/bin/env raku
unit sub MAIN (UInt $limit = 100);
my $ap := (1..Inf).grep( { .is-prime && .comb.sum.is-prime } );
my @ap;
loop
{
state $index = 0; # [1]
my $current = $ap[$index++];
last if $current > $limit;
@ap.push: $current;
}
@ap.join(", ").say;

[1] I have chosen to iterate (with an index) over the sequence.
Using a state variable (declared with `state`

) makes it possible to set
it up inside the loop, so that it is not visible outside.

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

.

Running it:

$ ./additive-primes
2, 3, 5, 7, 11, 23, 29, 41, 43, 47, 61, 67, 83, 89
$ ./additive-primes 110
2, 3, 5, 7, 11, 23, 29, 41, 43, 47, 61, 67, 83, 89, 101
$ ./additive-primes 47
2, 3, 5, 7, 11, 23, 29, 41, 43, 47

Looking good.

As zeekar pointed out in Reddit, we can (and indeed should) specify the upper limit in the starting range. That will give a much simpler program:

File: additive-primes2#! /usr/bin/env raku
unit sub MAIN (UInt $limit = 100);
say (1 ..^ $limit).grep({ .is-prime && .comb.sum.is-prime }).join(", ");

Running it gives the expected result:

$ ./additive-primes2
2, 3, 5, 7, 11, 23, 29, 41, 43, 47, 61, 67, 83, 89
$ ./additive-primes2 200
2, 3, 5, 7, 11, 23, 29, 41, 43, 47, 61, 67, 83, 89, 101, 113, 131, 137, \
139, 151, 157, 173, 179, 191, 193, 197, 199

#! /usr/bin/env perl
use strict;
use warnings;
use feature 'say';
use feature 'state';
use Math::Prime::Util 'is_prime';
use List::Util 'sum';
my $limit = $ARGV[0] || 100;
my @ap;
while (1)
{
state $current = 0;
last if $current++ >= $limit;
next unless is_prime($current);
my @digits = split(//, $current);
my $sum = sum(@digits);
push(@ap, $current) if is_prime($sum);
}
say join(", ", @ap);

Running it gives the expected result:

$ ./additive-primes-perl
2, 3, 5, 7, 11, 23, 29, 41, 43, 47, 61, 67, 83, 89
$ ./additive-primes-perl 200
2, 3, 5, 7, 11, 23, 29, 41, 43, 47, 61, 67, 83, 89, 101, 113, 131, 137,\
139, 151, 157, 173, 179, 191, 193, 197, 199

Write a script to compute first series `Cuban Primes <= 1000`

. Please
refer wikipedia page for more
informations.

7, 19, 37, 61, 127, 271, 331, 397, 547, 631, 919.

As a sequence first, just as for the «Additive Primes»:

File: fscp-seq#! /usr/bin/env raku
unit sub MAIN (UInt $length = 10);
my $fscp := (1..Inf).map( { 3 * $_ ** 2 + 3 * $_ + 1 } ).grep( *.is-prime );
$fscp.head($length).join(", ").say;

The expression is straight out of the wikipedia page.

Running it:

$ ./fscp-seq
7, 19, 37, 61, 127, 271, 331, 397, 547, 631
$ ./fscp-seq 20
7, 19, 37, 61, 127, 271, 331, 397, 547, 631, 919, 1657, 1801, 1951, 2269,\
2437, 2791, 3169, 3571, 4219

Looking good.

Adding an upper limit, instead of an element count, is easy. The additional code has been copied from «additive-primes»:

File: fscp#! /usr/bin/env raku
unit sub MAIN (UInt $limit = 1000);
my $fscp := (1..Inf).map( { 3 * $_ ** 2 + 3 * $_ + 1 } ).grep( *.is-prime );
my @fscp;
loop
{
state $index = 0;
my $current = $fscp[$index++];
last if $current > $limit;
@fscp.push: $current;
}
@fscp.join(", ").say;

Running it:

$ ./fscp
7, 19, 37, 61, 127, 271, 331, 397, 547, 631, 919
$ ./fscp 2000
7, 19, 37, 61, 127, 271, 331, 397, 547, 631, 919, 1657, 1801, 1951

Looking good.

Note that I have dropped the trailing period
(`.`

) that was specified in the challnge output, as the first part did not
have it.

#! /usr/bin/env perl
use strict;
use warnings;
use feature 'say';
use feature 'state';
use Math::Prime::Util 'is_prime';
use List::Util 'sum';
my $limit = $ARGV[0] || 1000;
my @fscp;
while (1)
{
state $current = 0;
my $mapped = 3 * $current ** 2 + 3 * $current++ + 1;
last if $mapped >= $limit;
push(@fscp, $mapped) if is_prime($mapped);
}
say join(", ", @fscp);

Running it gives the expected result:

$ ./fscp-perl
7, 19, 37, 61, 127, 271, 331, 397, 547, 631, 919

And that's it.