Raku Solutions Weekly Review
Getting in Touch
Email › Email me (Andrew) with any feedback about this review.
GitHub › Submit a pull request for any issues you may find with this page.
Twitter › Join the discussion on Twitter!
We’d greatly appreciate any feedback you’d like to give.
Welcome to the Raku Review for Week 084 of The Weekly Challenge!. For a quick overview, go through the original tasks and recap of the weekly challenge.
Task 1. Reverse Integer
In the first problem, the task is to take a number and print it backwards. A couple of additional requirements are: 1) to keep the sign and 2) to print 0 if the flipped number exceeds the size of a 32-bit integer.
Obviously, the most Rakuish solution is to use the flip
method. It works with strings, but Raku will implicitly translate numbers to it. To keep the sign, you can use the sign
method that returns +1
for positive numbers, -1
for negative numbers and 0
for 0
. Try it in the REPL shell:
$ raku
> 0.sign
0
> -20.sign
-1
> 20.sign
1
So, here is a possible solution:
my $r = $n.sign * $n.abs.flip;
say -2147483648 <= $r <= 2147483647 ?? $r !! 0;
Feng Chang split the task into the four clear options:
given $n {
when (0) { put 0 }
when (0 < $n ≤ imax) { put $n.flip; }
when (imin ≤ $n < 0) { put '-', (-$n).flip; }
default { put 0; }
}
Jan Krnavek used a combination of the above-mentioned methods:
with $n.abs.flip * $n.sign {
when -2³¹ ..^ 2³¹ { $_ };
default { 0 };
}
Mark Anderson uses smartmatch directly (without the given
block):
$N = $N.abs.flip * $N.sign;
my \MAX = 2**31;
say $N ~~ -MAX .. MAX-1 ?? $N !! 0;
Markus Holzer decided to use the subst
method to replace the digits in the number and thus to keep the minus sign in place:
say -2³¹ <= $i <= 2³¹
?? $i.subst( / \d+ /, + *.flip )
!! 0;
Notice how Stuart Little keeps the sign by removing the last character from what sign
returns:
return ($nr >= 2**31) ?? (0)
!! ($nr.sign.chop ~ $nr.abs.flip);
Of course, nobody stops you from determining the sign by comparing the number with 0, as Arne Sommer, Colin Crain, and Kang-min Liu did it:
my $sign = $N < 0
?? "-"
!! "";
my $new = $sign ~ $N.abs.flip;
. . .
my $sign = $num < 0 ?? -1 !! 1 ;
my $out = $sign * $num.abs.flip ;
. . .
my $o = ($n < 0 ?? -1 !! 1) * flip abs $n;
return (-2³¹ ≤ $o < 2³¹) ?? $o !! 0;
Roger Bell_West and Ulrich Rieke used regexes to extract the digits and the sign from the number:
my $r=$s.comb.reverse.join('');
if ($r ~~ /(<[0..9]>+)\-$/) {
$r="-$0";
}
. . .
$N ~~ /(<[+-]>*)(\d+)/ ;
my $maximum = 2147483647 ;
if ( $N.Int <= $maximum ) {
say ($0.Str ~ $1.Str.flip) ;
}
else {
say 0 ;
}
Video review
The full review of Task 1 is available on YouTube:
The timestamps for quick access to the review of each solution.
- 01:17 - Andrew Shitov
- 02:46 - Feng Chang
- 03:50 - Jan Krnavek
- 05:45 - Mark Anderson
- 06:35 - Markus Holzer
- 09:22 - Philip Hood
- 10:17 - Simon Proctor
- 11:10 - Stuart Little
- 12:35 - Arne Sommer
- 13:22 - Athanasius
- 14:24 - Colin Crain
- 15:26 - Jaldhar H. Vyas
- 16:48 - Julio de Castro
- 17:48 - Kang-min Liu
- 19:08 - Laurent Rosenfeld
- 20:41 - Myoungjin Jeon
- 24:51 - Roger Bell_West
- 27:18 - Ulrich Rieke
Task 2. Find Square
In the second task, you take a matrix with 1s and 0s and have to find the number of all possible squares, whose corners keep 1s.
To solve the task, you can scan each and every cell of the matrix, and from there ‘blow’ the squares of different sizes before you reach one of the edges. Here is an example of how you do it with three nested loops:
for ^$w -> $x {
for ^$h -> $y {
for 1..* -> $z {
last if $x + $z >= $w || $y + $z >= $h;
. . .
To test and count the squares, just check the values at the corresponding coordinates:
$count++ if all(
@m[$x; $y ], @m[$x + $z; $y ],
@m[$x; $y + $z], @m[$x + $z; $y + $z]
);
A number of participants noted that you could optimise this approach by skipping the entire third loop if you see that the cell at coordinates ($x
, $y
) is 0
. Indeed, all further checks will fail with such a corner.
Instead of nesting loops, you can use Raku’s cross operator X
to make the table of cell coordinates:
for 0..^@N[0].elems-1 X 0..^@N.elems-1 -> ($i, $j) {
. . .
Or:
multi find-square ( @n, ) {
my $rows = @n.elems;
my $columns = @n[0].elems;
^$rows X ^$columns
andthen .grep: -> ($x, $y) { @n[$x;$y]==1 }\
andthen .map: { find-square @n, :topleft($_) }\
andthen .sum
}
Another interesting approach is to — again! — use combinations
to test different square sizes. This fragment also uses the above-mentioned optimisation to skip the squares with 0 in the corner:
for ^@matrix.end -> $i {
my @indices = @matrix[$i].grep(1, :k);
next unless @indices.elems >= 2;
for @indices.combinations(2) -> [$h, $t] {
my $skip = $i + $t - $h;
next if $skip > @matrix.end;
$count++ if all @matrix[$skip][$h, $t];
}
}
There are more interesting bits both in the algorithms and in language usage, so I invite you to watch the video review and to look at the code more precisely.
Video review
The full review of Task 2 is available on YouTube:
The timestamps to the reviews of the individual solutions:
- 01:18 - Andrew Shitov
- 03:20 - Feng Chang
- 04:58 - Jan Krnavek
- 07:43 - Mark Anderson
- 13:19 - Markus Holzer
- 15:50 - Philip Hood
- 18:12 - Simon Proctor
- 20:28 - Stuart Little
- 22:42 - Arne Sommer
- 26:02 - Athanasius
- 29:35 - Colin Crain
- 31:26 - Jaldhar H. Vyas
- 33:31 - Julio de Castro
- 34:42 - Kang-min Liu
- 37:46 - Laurent Rosenfeld
- 39:00 - Myoungjin Jeon
- 44:00 - Roger Bell_West
If you want to participate in The Weekly Challenge, please contact us at perlweeklychallenge@yahoo.com.