PWC 240 Just a slice. No loop, please. I'll eat it here.
Published: 2023-10-25 16:33:50

[Perl Weekly Challenge 240](https://theweeklychallenge.org/blog/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](https://theweeklychallenge.org/blog/perl-weekly-challenge-240/#TASK1)
```text
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`.
```perl
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 (`$_`) with `substr` 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 the `map` 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:
```perl
my $priority = "critical";
my $message = "Assign all ". uc(critical) . " bugs to Bob";
```
But Perl can do case changes while evaluating the quotes:
```perl
my $message = "Assign all \U$priority\E bugs to Bob";
```
The case conversion functions are documented in [perldoc perlfunc](https://perldoc.perl.org/functions).  The case conversions in string interpolation are documented in [perldoc perlop](https://perldoc.perl.org/perlop#Quote-and-Quote-like-Operators) under "Quote and quote-like operators".

Extra-double-secret bonus super-nerd tip: [string substitution in `vim`](https://vimdoc.sourceforge.net/htmldoc/change.html#s/\u) also supports the Perl syntax of `\U` and `\L` for case conversion.

## [Task 2: Build Array](https://theweeklychallenge.org/blog/perl-weekly-challenge-240/#TASK2)
```text
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.
```perl
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).
```perl
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.