# RogerBW's Blog

Perl Weekly Challenge 64: summing paths and breaking words 10 June 2020

I’ve been doing the Perl Weekly Challenges. The latest involved searching for paths through a grid and breaking a word into tokens.

Given an m × n matrix with non-negative integers, write a script to find a path from top left to bottom right which minimizes the sum of all numbers along its path. You can only move either down or right at any point in time.

Another week, another breadth-first search with a FIFO buffer. Each buffer entry consists of the x,y coordinates of the current/next position, followed by the trail to date..

``````my @l=([0,0]);

my \$s;
my @p;

while (@l) {
my \$t=shift @l;
my @xy=splice @{\$t},0,2;
``````

Add to the trail the value of the current cell.

``````  push @{\$t},\$in[\$xy][\$xy];
``````

If this is the bottom right cell…

``````  if (\$t->[-1] == \$in[-1][-1]) {
``````

Here's the big inefficiency; I could keep the sum-to-date in that buffer too rather than calculating the whole thing each time.

``````    my \$sa=sum(@{\$t});
``````

But if that sum is less than the lowest sum found so far (or we haven't found one yet), set the lowest sum to this, and log the trail. (We only need one lowest-value trail, not all of them.)

``````    if (!defined \$s || \$sa < \$s) {
\$s=\$sa;
@p=@{\$t};
}
}
``````

In any case, if `(x+1,y)` lies inside the array, try that later.

``````  if (\$xy < \$#in && \$xy <= \$#{\$in[\$xy+1]}) {
push @l,[\$xy+1,\$xy,@{\$t}];
}
``````

If `(x,y+1)` lies inside the array, try that later too.

``````  if (\$xy < \$#{\$in[\$xy]}) {
push @l,[\$xy,\$xy+1,@{\$t}];
}
}
``````

Then print the output in the desired form.

``````binmode STDOUT,':utf8';
print "\$s ( ".join(' → ',@p)." )\n";
``````

I decided to beat Raku until it behaved.

``````my @l=((0,0),);

my \$s='';
my @p;

while (@l) {
``````

For once, `flat` seems to be enough.

``````  my @t=(shift @l).flat;
my @xy=splice @t,0,2;
push @t,@in[@xy][@xy];
``````

This is of course a much cleaner syntax than `\$in[-1][-1]` that we used in Perl.

``````  if (@t[@t.end] == @in[@in.end][@in[@in.end].end]) {
my \$sa=sum(@t);
if (\$s eq '' || \$sa < \$s) {
\$s=\$sa;
@p=@t;
}
}
``````

And the `map`s get the array-squashing job done.

``````  if (@xy < @in.end && @xy <= @in[@xy+1].end) {
push @l,(@xy+1,@xy,map {\$_},@t);
}
if (@xy < @in[@xy].end) {
push @l,(@xy,@xy+1,map {\$_},@t);
}
}

say "\$s ( " ~ join(' → ',@p) ~ " )";
``````

You are given a string `\$S` and an array of words `@W`.

Write a script to find out if `\$S` can be split into sequence of one or more words as in the given `@W`.

Print the all the words if found otherwise print 0.

There's no word-splitting here, just an attempt to construct the longer string from the shorter ones. And guess what, it's another breadth-first search with a FIFO buffer.

``````my \$sl=length(\$S);

my @l;
my \$done=0;

while (!\$done) {
my \$c=[];
if (@l) {
\$c=shift @l;
}
``````

`\$c` is the list of words used thus far; `\$cc` is their concatenation.

``````  my \$cc=join('',@{\$c});
foreach my \$wc (@W) {
``````

`\$ccw` is the concatenation of \$cc with the candidate word, and `\$ccwl` is its length.

``````    my \$ccw=\$cc.\$wc;
my \$ccwl=length(\$ccw);
``````

So if `\$ccw` is longer than the source string, it can be discarded. If it's exactly the same length and it matches, we have a solution. If it's shorter and it matches, add this sequence to the buffer for later extension.

``````    if (\$ccwl <= \$sl) {
if (index(\$S,\$ccw)==0) {
push @l,[@{\$c},\$wc];
if (\$ccwl == \$sl) {
\$done=1;
last;
}
}
}
}
unless (@l) {
last;
}
}
``````

The solution is in the last entry in `@l`.

``````if (@l) {
print join(', ',map {'"' . \$_ . '"'} @{\$l[-1]}),"\n";
} else {
print "0\n";
}
``````

Raku needs a bit more tweaking… `chars` instead of `length`, mostly, but also a two-stage test of `index` (because it now returns `Nil` rather than -1 when there's no match, which generates a warning if you use it in a numeric comparison)

``````    if (\$ccwl <= \$sl) {
with index(\$S,\$ccw) -> \$i {
``````

And then there's a whole intermediate variable needed in order to get the right structure pushed into the buffer.

``````        if (\$i==0) {
my @q=@c;
push @q,\$wc;
push @l,@q;
if (\$ccwl == \$sl) {
\$done=1;
last;
}
}
``````

The more Raku I write, the more I realise there is basically no project in which I'd choose to use it rather than any other language I know. The neat bits of syntax, while very neat indeed, are not for me sufficient compensation for the loss of CPAN when moving from Perl, and it's dog-slow to boot.