This is my response to the Perl Weekly Challenge #164.
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
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.