@foobar = reduce {[@^a ^+ @^b]} @foo, @bar, @baz, @etc;
How? Well, the @^a and @^b make the reduction block a curried subroutine, whose parameter list consists of two unflattened arrays. That is:
{[@^a ^+ @^b]}
is shorthand for:
sub (@a, @b) { return [@a ^+ @b] }
(modulo the currying (which isn't used here (so forget I mentioned it))).
With that parameter list on its reduction block, reduce would expect arrays instead of scalars as its remaining arguments.
Note that the square brackets are critical here, since reduce always applies the reduction in scalar context, but we need to get back something that can be treated like an array in the next reduction (i.e. an array reference).
That is: the result of the first reduction step has to be a scalar, but that result -- when it becomes the first argument to the reduction block in the second step of the reduction -- has to be an array. Perl 6's auto-intra-conversion of arrays and array refs makes that possible.
Having reduce and other list ops honour their block's parameter list would be very cool in other ways too:
# sum of maximal values...
$maxsum = reduce { $^a + max(@^b) } 0, @foo, @bar, @etc;
In this example the reduction block has two parameters: a scalar and an unflattened array. So it sucks corresponding elements from the argument list (i.e. 0 and @foo), finds the maximum value in @foo, and adds it to the partial sum. The result becomes the first argument of the next reduction step, which then grabs @bar for its second
argument, finds @bar's maximum and adds it in. That result
becomes the first argument to the third reduction, which grabs
@etc, and adds its maximum as well. Having exhausted all its arguments, reduce returns the final reduction result, which turns out to be the sum of
all maxima.
Pushing the envelope a little, we could also write:
# grep out hash entries whose key *or* value is 'moo'...
%cow = map { any($^key,$^val) eq 'moo' ?? $^key => $^val :: () } %
+animals;
Here, the mapping block is equivalent to:
sub ($key, $val) { ... }
So each mapping step would pull two scalar arguments off the map's argument list. If either is 'moo', the block returns the pair; otherwise it returns an empty list (which is flattened out of existence in the resultant list). The outcome is that the %cow hash is assigned a list of pairs, which become its entries.
Where to the scalar keys and values come from? They're flattened out of the %animals hash by the scalar contexts imposed by the two scalar arguments of the reduction block (!)
BTW, that map { $test ? $result : () } @data construct is a trick you can use in Perl 5 too. It's the infamous "grepping map" (i.e. a floor wax and a dessert topping).
Update: Fixed the missing second colon on the ternary operator in the last example. Thanks blakem! |