PWC 196

PWC 196

Aside on Advent of Code

I have done the Advent of Code challenges in Perl 5 up to #14 so far. Here are my solutions. I probably should stop there at least for now, since I have a lot of work despite the holidays. But let's see, #15 looks intriguing, and I'm sure the later ones are too.

Back to discussing our Weekly Challenges for this week. This week, I submitted only Perl 5 and Raku solutions, not Julia.

Challenge 1 (Pattern 132)

We are given a list of at least three integers. We are required to extract one sub-list of three integers that satisfies the following restrictions:

  1. If i,j,k respectively are the indices in the original list of the extracted items, then i < j < k.
  2. If a[i], a[j], a[k] respectively are the items in the sub-list in order, then we have: a[k] > a[i], and a[j] > a[k] (similar to the ordering in the list 1,3,2 whence the name).

This is a one-liner in Raku, using the combinations operator. I did not directly implement restriction 1 above, since it seems that combinations are extracted by default in the same order as they are in the original list. So the combinations operator should take care of this automatically.

Here is my one-liner:

raku -e 'my @list=@*ARGS; (0 .. @list-1).combinations(3).map({@list[$_]}).grep({ @_[2] > @_[0] }).grep({ @_[1] > @_[2] }).head(1).flat.say' $@

From the given list (command-line @*ARGS), extract combinations of 3 elements, and then grep those that satisfy the ordering requirement. If we have more than one result, keep only the first. Flatten before printing so that the output looks nicer (it's a nested list (( ... )) before flattening).

This logic translates directly to Perl 5, with the help of the combinations operator from Algorithm::Combinatorics, though I did not attempt a one-liner in Perl 5, and I have implemented the steps in a slightly different order. In Perl 5 of course, we do not have the left-to-right chaining that we have in Raku, so the logic is constructed from right to left. Also my syntax in Perl 5 yields a flat list of all the sub-lists one after the other (in Raku, we get a list of lists), so I just return the first three elements. Like Raku, the combinations operator seems to preserve the ordering from the original list, so that there is no need to explicitly code for restriction 1 above. Here is the key snippet in Perl 5:

   ( map {@$ra_list[@$_]}
    grep { ( ($$ra_list[$_->[1]]) > ($$ra_list[$_->[2]]) )  }
    grep { ( ($$ra_list[$_->[2]]) > ($$ra_list[$_->[0]]) )  }
    combinations([0 .. @$ra_list-1],3) ) [0 .. 2];

I use Data::Dumper in Perl 5 to print out my results, avoiding the minor chore of writing a subroutine to nicely print out the array.

Challenge 2 (Range list)

We are given a list of integers, which are both sorted in ascending numerical order, and do not contain any duplicates. We are asked to report all the complete closed ranges (of the form $a .. $b in Perl) that occur in the list, and report each of them in mathematical interval format as say [1,5] (not quite mathematically correct, since these are integers only, while that notation typically represents intervals over the reals. But never mind).

As with Challenge 1, I started with Raku and then translated to Perl 5. This time, I did not attempt a one-liner (or rather I tried and gave up fast). Using a pair of subroutines, I extract the sub-lists that form complete closed ranges and store them in an array, and then loop through that array to get the boundaries of each range and print them in the [a,b] format.

Here they are in Raku.

Getting the ranges (and then calling the printing sub to print them out as desired):

sub range-list( @array) {
    my @arry=@array.sort({$^a <=> $^b}).unique; #-- just in case ...
    @arry.unshift(-Inf).push(-Inf);
    &print-edges( @arry[(1 .. @arry-2).grep({(@arry[$_]-@arry[$_-1]==1) || (@arry[$_+1]-@arry[$_]==1)})] );    
}

I wrap the input array in one which is one element longer at both ends, so that I can loop through without Raku complaining about going out of bounds. It's not very difficult to compress into a single-line subroutine, but I'll stick with this more readable version.

And here is the print subroutine.

sub print-edges( @ranges ) {
    my $return-string = "([@ranges[0],";
    for (1 .. @ranges-2) -> $i {
        ( (@ranges[$i] - @ranges[$i-1]) > 1 ) &&
            ($return-string ~= (@ranges[$i-1] ~ "],[" ~ @ranges[$i] ~ ","));
    }
    #-- end the string
    $return-string ~= (@ranges[@ranges-1] ~ "])");
    $return-string;   
}

Comments

Popular posts from this blog

PWC 215

PWC 227

PWC 234