Perl Weekly Challenge: Week 40

Challenge 1:

Show Multiple Arrays Content

You are given two or more arrays. Write a script to display values of each list at a given index.

For example:

Array 1: [ I L O V E Y O U ]

Array 2: [ 2 4 0 3 2 0 1 9 ]

Array 3: [ ! ? £ $ % ^ & * ]

We expect the following output:

  I 2 !
  L 4 ?
  O 0 £
  V 3 $
  E 2 %
  Y 0 ^
  O 1 &
  U 9 *

Raku makes this easy with the [Z] operator.

multi sub MAIN() {
    my @array1 = < I L O V E Y O U >;
    my @array2 = < 2 4 0 3 2 0 1 9 >;
    my @array3 = < ! ? £ $ % ^ & * >;

    for [Z] @array1, @array2, @array3 -> @i {
        @i.join(q{ }).say;
    }
}

(Full code on Github.)

Perl doesn't have [Z] so I wrote a function to emulate it.

sub Z {
    my $numArrays = scalar @_;

    my @result;

    for my $i (0 .. scalar @{ $_[0] } - 1) {
        my @row;

        for my $j (0 .. $numArrays - 1) {
            push @row, $_[$j]->[$i];
        }

        push @result, \@row;
    }

    return @result;
}

The rest works the same as in Raku.

my @array1 = (qw/ I L O V E Y O U /);
my @array2 = (qw/ 2 4 0 3 2 0 1 9 /);
my @array3 = (qw/ ! ? £ $ % ^ & * /);

for my $i (Z(\@array1, \@array2, \@array3)) {
    say join q{ }, @{ $i };
}

(Full code on Github.)

Challenge 2:

Sort SubList

You are given a list of numbers and set of indices belong to the list. Write a script to sort the values belongs to the indices.

For example,

List: [ 10, 4, 1, 8, 12, 3 ]

Indices: 0,2,5

We would sort the values at indices 0, 2 and 5 i.e. 10, 1 and 3.

Final list would look like below:

List: [ 1, 4, 3, 8, 12, 10 ]

Perl solution first. These are my list and indices.

my @list = ( 10, 4, 1, 8, 12, 3 );
my @indices = ( 0, 2, 5 );

The answer is basically a one-liner but it is the kind of forbidding-looking code that gives Perl a bad reputation for readability though I must say it looks fine to me. In any case, in order to make it more legible, I've split the code over four lines.

map { state $i = 0; $list[$indices[$i++]] = $_; }
    sort { $a <=> $b }
        map { $list[$_] }
            @indices;

To explain it, we have to go in reverse order.

        map { $list[$_] }
            @indices;

...takes our list of indices and generates an array of values from the @list at those indices.

    sort { $a <=> $b }

...sorts that array numerically as the spec requires.

map { state $i = 0; $list[$indices[$i++]] = $_; }

... takes consecutive values from that array and assigns them back into the list at the appropriate place. But how do we know which is the appropriate place? Well, we have to step through @indices but we are currently stepping through the array of sorted values assembled above. So I defined a state variable (what is known as a static variable in C/C++) to act as a counter.

Finally, we display the now sorted list.

say join q{ }, @list;

(Full code on Github.)

Translating the Perl version into Raku was fairly straightforward. One small hiccup I had was in map()ing the array of sorted sublist values. I had to wrap that part in [] to make the map work on the right values. This is the final version:

my @list = < 10 4 1 8 12 3 >;
my @indices = < 0 2 5 >;

[ @indices.map({ @list[$_] }).sort ]
    .map({ state $i = 0; @list[@indices[$i]] = $_; $i++;});

@list.join(q{ }).say;

(Full code on Github.)