Perl Weekly Challenge: Week 305
Challenge 1:
Binary Prefix
You are given a binary array.
Write a script to return an array of booleans where the partial binary number up to that point is prime.
Example 1
Input: @binary = (1, 0, 1)
Output: (false, true, true)
Sub-arrays (base-10):
(1): 1 - not prime
(1, 0): 2 - prime
(1, 0, 1): 5 - prime
Example 2
Input: @binary = (1, 1, 0)
Output: (false, true, false)
Sub-arrays (base-10):
(1): 1 - not prime
(1, 1): 3 - prime
(1, 1, 0): 6 - not prime
Example 3
Input: @binary = (1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1)
Output: (false, true, true, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, true)
I solved this one by first defining a list variable to hold the results.
my @results;
Now for each index of @binary
's elements...
for 0 .. @binary.end -> $i {
...we join()
all the elements from 0 to the current one into one string which we can treat as a binary number
and convert back to base 10 with .parse-base(2)
. Then we check if it is a prime number with .is-prime()
. The result (true or false) is added to @results
with .push()
.
@results.push(@binary[0 .. $i].join.parse-base(2).is-prime);
}
When all the subarrays have been processed, the output is printed.
@results.join(q{ }).say;
The Perl version is equally straightforward.
my @results;
for my $i (0 .. (scalar @binary - 1)) {
The only annoying thing is that to make a string into a binary literal, you have to prefix 0b
in front of it
and parsing of it requires the unintuitively named oct()
function.
push @results, isPrime(oct('0b' . join q{}, @binary[0 .. $i]));
}
say join q{ }, map { $_ ? 'true' : 'false' } @results;
Challenge 2:
Alien Dictionary
You are given a list of words and alien dictionary character order.
Write a script to sort lexicographically the given list of words based on the alien dictionary characters.
Example 1
Input: @words = ("perl", "python", "raku")
@alien = qw/h l a b y d e f g i r k m n o p q j s t u v w x c z/
Output: ("raku", "python", "perl")
Example 2
Input: @words = ("the", "weekly", "challenge")
@alien = qw/c o r l d a b t e f g h i j k m n p q s w u v x y z/
Output: ("challenge", "the", "weekly")
For a change, I did the Perl version first. My script takes input from the command line with the first
argument becomes $alien
and the rest, @words
. So for example 2, the input would look like this:
"corldabtefghijkmnpqswuvxyz" "the" "weekly" "challenge"
The next step is to create a hash, called %order
whose keys are the individual letters in $alien
and
values are the position that letter occurs in. (i.e. c => 0
, o => 1
) etc.
my $i = 0;
my %order = map { $_ => $i++ } split //, $alien;
The next two lines are just for outputting the results.
say
join q{ },
The heart of the matter is here. sort()
can take a custom sorting subroutine so we can use the alien
dictionary order instead of the standard ASCII (well, Unicode actually) order.
sort {
The subroutine argument to sort()
is passed two elements of the array to be sorted at a time. They are
assigned the special variables $a
and $b
. We split()
theses into lists of single characters.
my @a = split //, $a;
my @b = split //, $b;
We loop over @a
comparing each element with the corresponding element in @b
. The elements are compared
using their %order
. If the orders are not equal, the smallest numerically is returned.
for my $i (keys @a) {
if ($order{$a[$i]} != $order{$b[$i]}) {
return $order{$a[$i]} <=> $order{$b[$i]};
}
}
If all characters compared so far are equal, we compare the lengths of @a
and @b
. This ensures that shorter words come before longer words if they are otherwise identical.
return @a <=> @b;
}
@words;
The Raku version is a little bit shorter.
We can simply the creation of %order
using .antipairs()
which does the same as the map()
block in the Perl
version.
my %order = $alien.comb.antipairs;
@words
.sort({
In Raku, instead of a loop, we can convert the entire lists of characters in one go as parameters to %order
.
Another quirk is that even though both lists consist of numbers, they are actually strings and not automatically
converted to numeric form so we have to use cmp
not <=>
to make the comparison.
%order{ $^a.comb } cmp %order{ $^b.comb };
})
.join(q{ })
.say;