with Raku

and Perl

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

Write a script to find all prime numbers less than 1000, which are also palindromes in base 10.
Palindromic numbers are numbers whose digits are the same in reverse. For example, **313**
is a palindromic prime, but **337** is not, even though **733** (337 reversed) is also
prime.

File: prime-palindrome

#! /usr/bin/env raku
unit sub MAIN ($limit = 1000); # [1]
(1 ..^ $limit).grep( *.is-prime ).grep({ $_ eq $_.flip }).join(", ").say;
# [1a] ###### # [2] ############ # [3] ################# # [4] ### # [5]

[1] "less than 1000", or any orther limit you may care to specify. We start with all the integers from 1 up to (but not including) that limit [1a].

[2] Then we use `grep`

to get rid of non-primes.

[3] Another `grep`

, this time to get rid of
non-palindromes (where we `flip`

the string and get the same result).

[4] Then we join the values together as a comma separated string,

[5] and print it.

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

.

Running it:

$ ./prime-palindrome
2, 3, 5, 7, 11, 101, 131, 151, 181, 191, 313, 353, 373, 383, 727, 757, 787, \
797, 919, 929
$ ./prime-palindrome 12000
2, 3, 5, 7, 11, 101, 131, 151, 181, 191, 313, 353, 373, 383, 727, 757, 787, \
797, 919, 929, 10301, 10501, 10601, 11311, 11411

`next`

statements to skip values (instead
of `grep`

). Chaining commands, as in Raku, is difficult due to the Perl
syntax. (And the result tends to be unreadable.)
File: prime-palindrome-perl
#! /usr/bin/env perl
use strict;
use warnings;
use feature 'say';
use Math::Prime::Util 'is_prime';
my $limit = int(shift(@ARGV) || 0) || 1000; # [1]
say $limit;
my @result;
for my $current (1 .. $limit -1)
{
next unless is_prime($current);
next unless $current eq reverse($current);
push(@result, $current);
}
say join(", ", @result);

[1] The innermost `||`

ensures that running the program without arguments
does not cause a warning (when we cast an undefined value to an integer). The outernost
`||`

ensures that zero (explicit and undefined from the innermost) and
non-numeric values are replaced with 1000.

Running it gives the same result as the Raku version:

$ ./prime-palindrome-perl
1000
2, 3, 5, 7, 11, 101, 131, 151, 181, 191, 313, 353, 373, 383, 727, 757, 787, \
797, 919, 929
$ ./prime-palindrome-perl 12000
12000
2, 3, 5, 7, 11, 101, 131, 151, 181, 191, 313, 353, 373, 383, 727, 757, 787, \
797, 919, 929, 10301, 10501, 10601, 11311, 11411

What if the reverse number should also be a prime (instead of palindromic). The reverse number has to be different from the number itself (i.e. not palindromic).

File: reverse-prime#! /usr/bin/env raku
unit sub MAIN ($limit = 1000);
(1 ..^ $limit).grep( *.is-prime )
.grep({ $_ ne $_.flip && $_.flip.is-prime }).join(", ").say;

Running it:

$ ./reverse-prime
13, 17, 31, 37, 71, 73, 79, 97, 107, 113, 149, 157, 167, 179, 199, 311, \
337, 347, 359, 389, 701, 709, 733, 739, 743, 751, 761, 769, 907, 937, 9\
41, 953, 967, 971, 983, 991

Write a script to find the **first 8 Happy Numbers** in base 10. For more information,
please check out Wikipedia.

Starting with any positive integer, replace the number by the sum of the squares of its digits, and repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle which does not include 1.

Those numbers for which this process end in 1 are happy numbers, while those numbers that do not end in 1 are unhappy numbers.

Example:

19 is Happy Number in base 10, as shown:

Starting with any positive integer, replace the number by the sum of the squares of its digits, and repeat the process until the number equals 1 (where it will stay), or it loops endlessly in a cycle which does not include 1.

Those numbers for which this process end in 1 are happy numbers, while those numbers that do not end in 1 are unhappy numbers.

Example:

19 is Happy Number in base 10, as shown:

19 => 1^2 + 9^2
=> 1 + 81
=> 82 => 8^2 + 2^2
=> 64 + 4
=> 68 => 6^2 + 8^2
=> 36 + 64
=> 100 => 1^2 + 0^2 + 0^2
=> 1 + 0 + 0
=> 1

Detecting an endless loop is difficult, so I have chosen to let the code run for a specified number of iterations, before giving in. That number is 100, which works out fine in practice. It can be overridden, if you want to. (Verbose mode verifies this, as the 8 happy numbers are detected in at most 5 steps - or 6 steps if we go for 1000 of them.)

File: happy-numbers#! /usr/bin/env raku
unit sub MAIN (:l(:$limit) = 8, :d(:$delta) = 100, :v(:$verbose)); # [0]
my @result;
for 1 .. * -> $number # [1]
{
my $n = $number; # [2]
my $i = $delta; # [3]
while $n != 1 && $i > 0 # [4]
{
$n = happy($n); # [5]
$i--; # [6]
}
if $verbose
{
say $n == 1
?? ": $number is happy (in { 100 - $i } step(s))"
!! ": $number is not happy";
}
@result.push($number) if $n == 1; # [7]
last if @result.elems == $limit; # [8]
}
say @result.join(", "); # [9]
sub happy ($number) # [10]
{
return $number.comb.map( * ** 2).sum; # [10a]
}

[1] Iterate over the numbers to check; from 1 and up. The exit strategy is decided by the number of matching numbers, set in [0] and checked in [8]

[2] As we are changing (transforming) the number, and still neeed the original one - as that is the number to report.

[3] The number of iterations before giving in. Also a copy, as we use this as a new counter for each number (in [1]). The default value is 100 (set in [0]), but you can override it if you want to.

[4] As long as we have not found the number to be happy (`$n != 1`

) and have not
exceeded the number of transformations to try (`$i > 0`

).

[5] Do another transformation (happify the number, so to speak).

[6] Count down the number of iterations left (before we give in).

[7] Add the number if we have reached 1 (i.e. the number is happy).

[8] Stop looking for additional happy numbers, if we have fulfilled the quota.

[9] Print the result; the list of 8 (or another amount) first happy numbers.

[10] Transform the number to the next level. It may be happy, and it may not. Square each digit, and add them together to get the transformation [10a].

Running it:

$ ./happy-numbers
1, 7, 10, 13, 19, 23, 28, 31

With verbose mode:

$ ./happy-numbers -v
: 1 is happy (in 0 step(s))
: 2 is not happy
: 3 is not happy
: 4 is not happy
: 5 is not happy
: 6 is not happy
: 7 is happy (in 5 step(s))
: 8 is not happy
: 9 is not happy
: 10 is happy (in 1 step(s))
: 11 is not happy
: 12 is not happy
: 13 is happy (in 2 step(s))
: 14 is not happy
: 15 is not happy
: 16 is not happy
: 17 is not happy
: 18 is not happy
: 19 is happy (in 4 step(s))
: 20 is not happy
: 21 is not happy
: 22 is not happy
: 23 is happy (in 3 step(s))
: 24 is not happy
: 25 is not happy
: 26 is not happy
: 27 is not happy
: 28 is happy (in 3 step(s))
: 29 is not happy
: 30 is not happy
: 31 is happy (in 2 step(s))
1, 7, 10, 13, 19, 23, 28, 31

Note the low number of steps (transformations) required for the happy numbers.

#! /usr/bin/env perl
use strict;
use warnings;
use feature 'say';
use Getopt::Long;
use List::Util qw(sum);
use feature 'signatures';
no warnings qw(experimental::signatures);
my $limit = 8;
my $delta = 100,
my $verbose = 0;
GetOptions("limit" => \$limit, "delta" => \$delta, "verbose" => \$verbose);
my @result;
my $number = 0;
while (++$number) # [1]
{
my $n = $number;
my $i = $delta;
while ($n != 1 && $i > 0)
{
$n = happy($n);
$i--;
}
if ($verbose)
{
$n == 1
? say ": $number is happy (in " . ( 100 - $i ) . " step(s))"
: say ": $number is not happy";
}
push(@result, $number) if $n == 1;
last if @result == $limit;
}
say join(", ", @result);
sub happy ($number)
{
return sum(map { $_ ** 2 } split(//, $number));
}

[1] Note the prefix incrementation, as the postfix version (which is more usual) would cause the loop to not run at all.

Running it gives the same result as the Raku version:

$ ./happy-numbers-perl
1, 7, 10, 13, 19, 23, 28, 31

And that's it.