Raku Solutions Weekly Review
Task 1
The first task was to find and display the number of either positive or negative integers, whatever is larger.
Let us start from the easy part. In Raku, there is a a built-in max
function (or method if you want to use an object notation: @array.max
). So, the code is:
say max($neg, $pos);
where $neg
and $pos
are the numbers of negative and positive data items in the original array.
Now, the second part is to indeed get the number of positives and negatives. At least three methods come to mind:
1: for
Scan the array using for
and count the numbers:
my @data = -3, 1, 2, -1, 3, -2, 4;
my $neg = 0;
my $pos = 0;
for @data -> $value {
$neg++ if $value > 0;
$pos++ if $value < 0;
}
say max($neg, $pos); # 4
This approach is used in the following solutions by:
2: grep
The second way is to use grep
, again as a method or as a function. For example:
say max(
(@data.grep: * > 0).elems,
(@data.grep: * < 0).elems
); # 4
If you prefer a more explicit syntax, use codeblocks instead of the WhatEver *
magic:
say max(
+(@data.grep({$_ > 0})),
+(@data.grep({$_ < 0}))
); # 4
(Just to compensate the absense of magic, a unary +
is used here to count the number of elements after each grep
.)
A few people used this approach including myself:
- Andrew Shitov
- Arne Sommer
- BarrOff
- Feng Chang
- Laurent Rosenfeld
- Luca Ferrari
- Packy Anderson — using a
map
to convert the comparison with0
to a Boolean equivalent:my $pos = [+] @ints.map({ $_ > 0 ?? 1 !! 0 });
- Roger Bell_West
- Ulrich Rieke
3: classify
The most sophisticated—and at the same time simple—way is to use the classify
routine, which already exists in Raku and is available without loading any modules. It gives the result in one go.
To see how it works with our input data, let’s add a zero element to it and dump the result of classify
:
my @data = -3, 1, 2, -1, 3, -2, 4, 0;
dd (@data.classify: * <=> 0);
This program prints:
(my Any %{Any} = Order::Less => $[-3, -1, -2], Order::Same => $[0], Order::More => $[1, 2, 3, 4])
You can see that there are three elements classified as Order::Less
—those which are less than 0
, four elements were marked as Order::More
, and the zero item gave Order::Same
, all in accordance to our condition * <=> 0
.
Putting it back and getting the maximum of the clusters, here’s a possible program:
my @data = -3, 1, 2, -1, 3, -2, 4;
my $c = @data.classify: * <=> 0;
# Hash[Any,Any] $c = $(my Any %{Any} = Order::Less => $[-3, -1, -2], Order::More => $[1, 2, 3, 4])
my $d = $c.map: *.value.elems;
# Seq $d = $((4, 3).Seq)
dd $d.max; # 4
In the comments, you can see what the program computes on every step.
The submitted solutions utilising classify
:
4: Bag!
In the submitted solutions, there is another interesting approach that uses Bag
s. Perl does not directly support bags, so maybe that’s why they often come to mind later rather than earlier when solving tasks. When we do not need the key—value pairs as in hashes, Bag
is a better choice. In our case, the value will be the number of items with the same magnitude that were put into the bag.
The core idea of the solution suggested by Bruce Gray is the following:
my ( $negatives, $zeros, $positives ) = @ns».sign.Bag{-1, 0, 1};
The input data @ns
is first converted to an array that has the values -1
, 0
, or 1
depending on the sign of the data item. For the first test sample it will be:
Array element = [-1, 1, 1, -1, 1, -1, 1]
The next step is to coerce this array to a bag and find the maximum:
return $negatives max $positives;