Perl Weekly Challenge: Week 279
Challenge 1:
Sort Letters
You are given two arrays,
@letters
and@weights
.Write a script to sort the given array
@letters
based on the@weights
.
Example 1
Input: @letters = ('R', 'E', 'P', 'L')
@weights = (3, 2, 1, 4)
Output: PERL
Example 2
Input: @letters = ('A', 'U', 'R', 'K')
@weights = (2, 4, 1, 3)
Output: RAKU
Example 3
Input: @letters = ('O', 'H', 'Y', 'N', 'P', 'T')
@weights = (5, 4, 2, 6, 1, 3)
Output: PYTHON
I did manage to get this into one line but I'm not completely satisfied as I think it should be possible to get it a little bit shorter.
%(@*ARGS[1].words Z=> @*ARGS[0].words).sort.map({ $_.value }).join.say
The input is taken from two command-line parameters; what the spec calls @letters
and @weights
are @*ARGS[0]
and @*ARGS[1]
respectively. Each parameter is a string of letters or numbers separated by spaces.
The first step is to convert each string into an array with .words()
. Then the Z=>
operator takes consecutive elements from both arrays and makes them into a List
of key-value Pairs
. %( ... )
around this operation turns the List
into a Hash
. We could also have used .Hash
but this is a little shorter.
Once we have this hash, we sort it and get the sorted values with .map()
. I was hoping there would be a more
direct way of doing this but I was unable to figure it out. These sorted values are then concatenated with .join()
and the resulting string is printed out with .say()
.
The Perl version is a lot longer mainly because Perl doesn't have all the shortcuts Raku has for assembling the hash. The six lines below do just that.
my @letters = split /\s+/, $ARGV[0];
my @weights = split /\s+/, $ARGV[1];
my %h;
for my $key (keys @weights) {
$h{$weights[$key]} = $letters[$key];
}
Once we have a hash though we can do everything else in one line.
say join q{}, map { $h{$_} } sort { $a <=> $b } keys %h;
Challenge 2:
Reverse Word
You are given a string,
$str
.Write a script to split the given string into two containing exactly same number of vowels and return true if you can otherwise false.
Example 1
Input: $str = "perl"
Output: false
Example 2
Input: $str = "book"
Output: true
Two possible strings "bo" and "ok" containing exactly one vowel each.
Example 3
Input: $str = "good morning"
Output: true
Two possible strings "good " and "morning" containing two vowels each or "good m" and "orning" containing two vowels each.
The spec talks about splitting strings but this is misleading. You will only get the exactly same number of vowels if the total number of vowels in the string is divisible by two. So all you need to is count them and see if the total is even (true) or odd (false.)
This can be done with regular expressions but a Perl trick I learned a long long time ago is that tr///
which is for replacing or deleting characters returns the count of characters replaced (or deleted). You can even save one character by using the synonym y///
. So all we need to do is get the input string as the first command-line parameter and run it through an invocation of y///
that replaces all vowels (ny code doesn't deal with upper-case but nor do any of the examples.) by nothing. The result of this is tested for evenness with the modulo operator %
and false or true is printed accordingly.
say $ARGV[0] =~ y/aeiou// % 2 ? "false" : "true"
The Raku version is a little bit longer. tr///
works differently in Raku so I used the regular expression method
instead. .match()
captures all (with the :g
or global flag) instances of the class a, e, i, o, and u. .elems()
counts how many captures and the integer modulo operator %%
is used to determine if that count is divisible by tw0 (true) or not (false) and say()
prints the answer out.
say @*ARGS[0].match(/(<[aeiou]>)/, :g).elems %% 2