Perl Weekly Challenge 240 gives us exercises in array operations. A quick read suggests that we'll be looping over array elements, but Perl lets us treat arrays in bulk.
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
Exposition
Creating an acronym can be looked at as doing a transformation on each element of a list: take the first character and convert it to lower-case. A solution for every language since Algol would involve using a loop to index the elements of the array, but we can do better in Perl. This is a perfect application for map
.
sub acronym($chk, @str)
{
return $chk eq join("", map { lc substr($_, 0, 1) } @str)
}
This reads backwards and inside-out, so in case it's not obvious, let's disassemble it.
-
map {...} @str
-- we're going to apply a transformation to every element of the @str array -
{ lc substr($_, 0, 1) }
-- the transformation we're applying is to take the first character of the array element ($_
) withsubstr
and then take the lower-case version of that character. Perl nicely handles UTF8 characters here, so this will work with things like accented characters and Greek letters. -
join("", map...)
-- the result of themap
will be a list of single-character strings.Join
-ing them with an empty string will produce the acronym. -
return $chk eq ...
-- the only thing remaining then is to do the comparison and return the boolean value
Digression
Using lc
reminds me of Perl's built-in letter-casing functions. The complement of lc
(lower-case) is uc
(upper-case). There's also the lesser known ucfirst
and lcfirst
, which operate on the first letter of a string.
Even more obscure is that upper-casing and lower-casing can be specified in string interpolation of quoted strings. Suppose we have a string variable, and we want the upper-case version of it inside a message string. The most obvious (to me) solution is to construct a string using the uc
function:
my $priority = "critical";
my $message = "Assign all ". uc(critical) . " bugs to Bob";
But Perl can do case changes while evaluating the quotes:
my $message = "Assign all \U$priority\E bugs to Bob";
The case conversion functions are documented in perldoc perlfunc. The case conversions in string interpolation are documented in perldoc perlop under "Quote and quote-like operators".
Extra-double-secret bonus super-nerd tip: string substitution in vim
also supports the Perl syntax of \U
and \L
for case conversion.
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)
Exposition
We could take this problem statement literally and make a loop over the indices of @int
. But this problem has a simplifying quirk: the action of every loop iteration will be nothing more than an indirect index of the array into itself.
my @new;
for ( my $i = 0 ; $i <= $#int ; $i++ )
(
$new[$i] = $int[$int[$i]]
)
We can take all those indexes in one group using an array slice. Instead of using a single integer as an index, and getting back a single scalar value, we can use a list of index values and get back a list of corresponding values. The task becomes an almost trivial one-liner (trivial, that is, if array slices are obvious to you).
sub buildArray(@int)
{
return [ @int[@int] ];
}
As usual, I prefer to use array references when returning an array from subroutine calls, because (1) that saves the overhead cost of copying lists, and (2) array references are easier to test using unit test frameworks.
Top comments (2)
I'm not sure what version of Perl you are using, but for me (and according to Perldocs), ucfirst only applies to the first character of the string
You're right. I conflated some things I was trying with Perl and title case in Vim and got sideways.