TWC Episode 154 - Padawan Missing

In which we search for a needle in a lendee
(or maybe a chatchka in a haystack),
and delight in some lazy CPAN comfort.

TWC Task #1 - Missing Permutation

Find all permutations missing from a list.

  • I have snipped most of the task permutations to make the code fit better in the blog post.

  • I don't want to write my own permutation code again.

  • Raku has built-in .permutations method, and (-) set-difference operator.

  • Perl has several CPAN modules for permutations; List::Permutor is the first one Google returned, and its ->next method allowed me to write a loop that was different than my Raku solution.

  • Python has Sets, and the itertools library handles permutations. itertools has lots of features that I miss when working outside of Raku, but they cannot be combined as nicely as Raku's, due to the basic(underlying(Python(function(call(syntax())))).

Raku

The partial list of juggled letters is in @in.

my @in = <PELR PREL ...snip...

Take the first word; we could have used any of them.

With no argument, .comb splits into a list of single characters.

.permutations gives all the possible rearrangements of those characters.

The ». makes a hyper method call that will be run on each item in the list of permuted characters, joining them back into words.

my @all = @in[0].comb.permutations».join;

When we do a set operation on a List, it is automatically converted to a Set.

(-) is the ASCII version of the set difference operator; it returns a Set of items present in the left-hand Set that are absent from the right-hand Set.

Iterating over the resulting Set gives us Pair objects, where the .value is always True, and the .key is the part we are interested in.

say .key for @all (-) @in;

Perl

The Perl version of Raku's Set is a hash,
initialized via my %h = map { $_ => 1 } @stuff;.

use List::Permutor;
my @in = qw<PELR PREL ...snip...>;
my %in_set = map { $_ => 1 } @in;
my $permutor = List::Permutor->new( split '', $in[0] );
while ( my @letters = $permutor->next() ) {
    my $word = join '', @letters;
    say $word if not $in_set{$word};
}

Python

The Python code mirrors the Perl solution. When given only a single string, list breaks it into characters.

In hindsight, I really should have named the last variable word instead of s.

from itertools import permutations

input_words = "PELR PREL ...snip...".split()
input_set = set(input_words)

for i in permutations(list(input_words[0])):
    s = "".join(i)
    if s not in input_set:
        print(s)

TWC Task #2 - Padovan Prime

Compute the first 10 distinct prime Padovan Numbers.

  • I don't want to write my own is_prime() code again.

Raku

There were several ways to write the code block. I chose one that highlights $c being requested then deliberately unused.

.squish only suppresses consecutive duplicates, so it works efficiently with lazy lists.

constant @Padovan = 1, 1, 1, { sink $^c; $^a + $^b } ... *;

say @Padovan.grep(&is-prime).squish.head(10);

Perl

I was very happy to discover List::Lazy, which make easy both the generation of the Padovan Numbers, and filtering them for primes.

It was going to be more trouble than it was worth to make a version of Raku's .squish that would work with List::Lazy, so I used the foreknowledge that there is exactly one duplicate, and just called uniq on the 10+1 numbers returned by ->next.

use List::Util qw<uniq head>;
use List::Lazy qw<lazy_list>;
use ntheory    qw<is_prime>;

my $Padovan = lazy_list {
    push  @$_, $_->[-2] + $_->[-3];
    shift @$_;
} [1, 1, 1];

my $prime_pad = $Padovan->grep( sub { is_prime($_) } );

say join ', ', uniq $prime_pad->next(11);

Python

I am pleased with the brevity of the Padovan() generator.

head() is from an itertools recipe; it is not my own code.

Comparing the final print line to the second line of the Raku code makes me wish for Raku's flexibility of choosing method calls vs function calls.

from sympy import isprime
from itertools import islice


def Padovan():
    p = [1, 1, 1]

    while True:
        p.append(p[-2] + p[-3])
        yield p.pop(0)


def squish(a):
    last = None
    for i in a:
        if i != last:
            yield i
        last = i


def head(n, iterable):
    return list(islice(iterable, n))


print(head(10, squish(filter(isprime, Padovan()))))

Primes, Primes, everywhere a Prime.
Making all the Integers, alone or combined
Sieve N, to the square root.
Can't you see its Prime?
-- audio by the Five Man Electric Band

Leave a comment

About Bruce Gray

user-pic "Util" on IRC and PerlMonks. Frequent speaker on Perl and Raku, but infrequent blogger.