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
&withis 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 aSlipis 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