This Perl Weekly Challenge didn’t lend itself to a song. Drat.
My learning Elixir is going to have to wait a few weeks; I’ve added some theatrical tech work to my evenings, so until I’m done with that, I’m just writing these solutions in the languages I know well.
Task 1: Acronym
You are given an array of strings and a check string.
Write a script to find out if the check string is the acronym of the words in the given array.
Example 1
Input: @str = ("Perl", "Python", "Pascal")
$chk = "ppp"
Output: true
Example 2
Input: @str = ("Perl", "Raku")
$chk = "rp"
Output: false
Example 3
Input: @str = ("Oracle", "Awk", "C")
$chk = "oac"
Output: true
Approach
Pretty straightforward: go through each of the items in @str
, grab the first character, append it to a string, then lowercase the result and compare it to the value of $chk
. Easy-peasy.
Raku
Once again, I wanted to use Raku’s Reduction Metaoperator, [ ]
, because it’s just so neat… but at first I couldn’t think of a way to get it to append the first character from each element without it just becoming the first character of the first and last elements. So I did this:
sub makeAcronym(@str) {
my $acronym;
for @str -> $s {
$acronym ~= substr($s, 0, 1);
}
return $acronym.lc;
}
But then I was reading more that the reduction metaoperator gives the same result as the reduce routine, so I went and read up on that. And I saw the following:
When the list contains no elements, an exception is thrown, unless
&with
is an operator with a known identity value (e.g., the identity value ofinfix:<+>
is 0). For this reason, you’re advised to prefix the input list with an initial value (or explicit identity value):my \strings = "One good string!", "And one another good string!"; say reduce { $^a ~ $^b }, '', |strings; # like strings.join my \numbers = 1, 2, 3, 4, 5; say reduce { $^a > $^b ?? $^a !! $^b }, 0, |numbers; # like numbers.max sub count-and-sum-evens( (Int \count, Int \sum), Int \x ) { x %% 2 ?? (count+1, sum+x) !! (count, sum) } say reduce &count-and-sum-evens, (0, 0), |numbers; # OUTPUT: «(2 6)»
Well, that made me think: if I reduced { $^a ~ substr($^b, 0, 1) }
and made an empty string the first element of the list I was reducing, then the first time through the reduction my $^a
string would be an empty string, and each subsequent time it would be the entirety of the acronym I was building. This got me to a new version:
sub makeAcronym(@str) {
my $acronym = reduce { $^a ~ substr($^b, 0, 1) },
('', |@str);
return $acronym.lc;
}
The |@str
part took me a little while to grok: because ('', @str)
winds up being represented internally as ['', [ 'one', 'two', 'three' ] ]
, I needed some way to flatten @str
when it was being added to the list. I thought ('', @str.flat)
would do what I wanted, but it didn’t. It turns out that if I was going to use .flat
I needed to use ('', @str)
. But then I realized it was staring me in the face in the documentation: .flat
|
. This is a quick way to do a Slip:
Sometimes you want to insert the elements of a list into another list. This can be done with a special type of list called a Slip.
…
Another way to make aSlip
is with the|
prefix operator. Note that this has a tighter precedence than the comma, so it only affects a single value, but unlike the above options, it will break Scalars.say (1, |(2, 3), 4) eqv (1, 2, 3, 4); # OUTPUT: «True» say (1, |$(2, 3), 4) eqv (1, 2, 3, 4); # OUTPUT: «True» say (1, slip($(2, 3)), 4) eqv (1, 2, 3, 4); # OUTPUT: «True»
I was being thrown by the sample code in the reduce routine documentation because it didn’t use a @
sigil for its arrays.
Since I’d gotten it to work with reduce
, could I get it to work with a reduction metaoperator? Sure. But because it wasn’t a single operator, I would need to wrap a call to a function instead:
sub firstOfSecond { $^a ~ substr($^b, 0, 1) };
sub makeAcronym(@str) {
my $acronym = [[&firstOfSecond]] ('', |@str);
return $acronym.lc;
}
I like that solution a lot. View the entire Raku script for this task on GitHub.
Perl
It turns out my Perl-fu is still greater than my Raku-fu, because turning the Raku code into Perl code just fell from my fingers:
use List::Util qw( reduce );
sub makeAcronym {
my $str = shift;
my $acronym = reduce { $a . substr($b, 0, 1) } '', @$str;
return lc($acronym);
}
Mostly, it’s Perl’s proclivity to generate lists from scalars or arrays separated by commas. I didn’t have to do anything fancy to make sure that '', @$str
was flattened to a list of scalars.
View the entire Perl script on GitHub.
Python
Because last week I was using functools.reduce, it was fresh on my mind.
from functools import reduce
def makeAcronym(str_list):
# add empty string to beginning of list
str_list = [''] + str_list
acronym = reduce(lambda a, b: a + b[0], str_list)
return acronym.lower()
View the entire Python script for this task on GitHub.
Task 2: Build Array
You are given an array of integers.
Write a script to create an array such that new[i] = old[old[i]] where 0 <= i < new.length.
Example 1
Input: @int = (0, 2, 1, 5, 3, 4)
Output: (0, 1, 2, 4, 5, 3)
Example 2
Input: @int = (5, 0, 1, 2, 3, 4)
Output: (4, 5, 0, 1, 2, 3)
Approach
This one feels weird. I think its the old[old[i]]
construction that makes it a little confusing. I’m hoping that given the array old
I’ll just be able to use old[old[i]]
as I loop through the indices of the array and it will just produce the desired output.
I also don’t know why it specifies new.length
instead of old.length
, because if we’re building the array new
, we don’t really know the length of the array before we start, and if we tried to have i >= old.length
we’d get an out-of-bounds error (whether it was caught or not) when we tried to access old[i]
. Oh, well.
Raku
sub buildArray(@old) {
my @new;
for 0 .. @old.elems - 1 -> $i {
@new.push(@old[@old[$i]]);
}
return @new;
}
And it does produce the desired output. View the entire Raku script for this task on GitHub.
Perl
sub buildArray(@old) {
my @new;
foreach my $i (0 .. $#old) {
push @new, $old[$old[$i]];
}
return @new;
}
One thing I like about Perl over Raku is being able to say $#old
to get the index of the last element of an array instead of @old.elems - 1
. View the entire Perl script on GitHub.
Python
def buildArray(old):
new = []
for i in range(0, len(old)):
new.append(old[old[i]])
return new
View the entire Python script for this task on GitHub.
Here’s all my solutions in GItHub: https://github.com/packy/perlweeklychallenge-club/tree/master/challenge-240/packy-anderson