Perl Weekly Challenge: Week 264
Challenge 1:
Greatest English Letter
You are given a string,
$str
, made up of only alphabetic characters[a..zA..Z]
.Write a script to return the greatest english letter in the given string.
A letter is greatest if it occurs as lower and upper case. Also letter ‘b’ is greater than ‘a’ if ‘b’ appears after ‘a’ in the English alphabet.
Example 1
Input: $str = 'PeRlwEeKLy'
Output: L
There are two letters E and L that appears as lower and upper.
The letter L appears after E, so the L is the greatest english letter.
Example 2
Input: $str = 'ChaLlenge'
Output: L
Example 3
Input: $str = 'The'
Output: ''
No one-liners this week but the Raku solution is still pretty concise.
We start by taking the input and splitting it into individual characters with .comb()
. Then we use .classify()
as we did
last week to put the upper and lower case characters into separate keys ('upper' and 'lower') of a hash called %chars
.
$str.comb.classify({ $_ ~~ 'A' .. 'Z' ?? 'upper' !! 'lower'}, :into(my %chars));
Treating the values of %chars<upper>
and %chars<lower>
as Set
s, the letters that they have in common will be the intersection of those sets. We can find the intersection with the ∩
operator but there is one additional step we must take first; The two sets
must contain the same types of elements so we transform the elements of the lower case set to upper case with .map()
and .uc()
.
The intersection is also a Set
so we need the .keys()
method to extract its' elements (i.e. the letters). .sort[*-1]
gives the he greatest (i.e. highest alphabetical value) letter. One last scenario to consider is if the intersection is empty which means there were no great letters in the string. // q{''}
prints empty quotes if that is the case.
say (%chars<upper>.values ∩ %chars<lower>.values.map({ .uc })).keys.sort[*-1] // q{''};
In Perl we don't have .classify()
so we have to recreate it ourselves. First, I declared two hashes to hold the upper and lower
case characters.
my %upper;
my %lower;
Then after splitting the input into individual characters, for each character...
for my $char (split //, $str) {
...If it is upper case, it is added as a key into the %upper
hash with the arbitrary value of 1. The value doesn't matter,
we just need the key to exist.
if ($char =~ /[[:upper:]]/) {
$upper{$char} = 1;
If the character wasn't upper case, it is added to %lower
.
} else {
$lower{$char} = 1;
}
}
A variable is reserved to hold the current greatest letter.
my $greatest = undef;
Then we iterate through the sorted keys of %upper
.
for my $char (sort keys %upper) {
For each one, we convert it to lower case and see if a key with that value exists in %lower
. The method I used to convert
to lower case using ord()
and chr()
relies on the fact that the numeric values of lower case ASCII characters are exactly 32
(the numeric values of the space character) more than their upper-case counterparts. It is needlessly complicated and honestly I
don't know what I was thinking. I could have just used lc()
. In fact in hindsight I see I could have just had upper-case keys in
%lower
.
if (exists $lower{chr((ord $char) + (ord ' '))}) {
Anyway if the upper-case letter has a lower-case counterpart, it becomes the new current greatest letter. $greatest = $char; } }
Whatever is the $greatest
after we've gone through all the letters in %upper
is the final answer and is printed out. If it was
still undef
that means we didn't find a greatest letter so empty quotes are printed.
say $greatest // q{''};
Challenge 2:
Target Array
You are given two arrays of integers,
@source
and@indices
. The@indices
can only contains integers0 <= i < size of @source
.Write a script to create target array by insert at index
$indices[i]
the value$source[i]
.
Example 1
Input: @source = (0, 1, 2, 3, 4)
@indices = (0, 1, 2, 2, 1)
Output: (0, 4, 1, 3, 2)
@source @indices @target
0 0 (0)
1 1 (0, 1)
2 2 (0, 1, 2)
3 2 (0, 1, 3, 2)
4 1 (0, 4, 1, 3, 2)
Example 2
Input: @source = (1, 2, 3, 4, 0)
@indices = (0, 1, 2, 3, 0)
Output: (0, 1, 2, 3, 4)
@source @indices @target
1 0 (1)
2 1 (1, 2)
3 2 (1, 2, 3)
4 3 (1, 2, 3, 4)
0 0 (0, 1, 2, 3, 4)
Example 3
Input: @source = (1)
@indices = (0)
Output: (1)
This one was really easy because once we have imported the sources and indices from command-line parameters
(they look like e.g. "0 1 2 3 4" "0 1 2 2 1"
for example 1.)...
my @sources = $s.split(/\s+/).map({ .Int });
my @indices = $i.split(/\s+/).map({ .Int });
...And declared a variable to hold the output...
my @output;
...we can traverse through @sources
by index and use it together with the equivalent index in @indices
as parameters to .splice()
which does all the work of inserting the number in the right place in @output
.
for @sources.keys -> $i {
@output.splice(@indices[$i], 0, @sources[$i]);
}
And then we print @output
formatted in the style of the example output.
say q{(}, @output.join(q{, }), q{)};
The Perl version is almost exactly equivalent.
my @sources = split /\s+/, $ARGV[0];
my @indices = split /\s+/, $ARGV[1];
my @output;
for my $i (keys @sources) {
splice @output, $indices[$i], 0, $sources[$i];
}
say q{(}, (join q{, }, @output), q{)};