Documentation for routine reduce
assembled from the following pages:
Class: Any §
From Any
(Any) routine reduce §
Defined as:
multi method reduce(Any: & --> Nil)multi method reduce(Any: )multi sub reduce (, +list)
This routine combines the elements in a list-y object, and produces a single result, by applying a binary subroutine. It applies its argument (or first argument for the sub form) as an operator to all the elements in the object (or second argument for the sub form), producing a single result. The subroutine must be either an infix operator or take two positional arguments. When using an infix operator, we must provide the code object of its subroutine version, i.e., the operator category, followed by a colon, then a list quote construct with the symbol(s) that make up the operator (e.g., infix:<+>
). See Operators.
say (1..4).reduce(:<+>); # OUTPUT: «10» say reduce :<+>, 1..4; # OUTPUT: «10» say reduce , 1..4; # OUTPUT: «1» sub hyphenate(Str \a, Str \b) say reduce , 'a'..'c'; # OUTPUT: «a-b-c»
Applied to a class, the routine will always return Nil
.
say Range.reduce(:<+>); # OUTPUT: «Nil» say Str.reduce(:<~>); # OUTPUT: «Nil»
See List.reduce for a more thorough discussion.
Class: Supply §
From Supply
(Supply) method reduce §
Defined as:
method reduce(Supply: --> Supply)
Creates a "reducing" supply, which will emit a single value with the same semantics as List.reduce.
my = Supply.from-list(1..5).reduce();.tap(-> ); # OUTPUT: «15»
Class: List §
From List
(List) routine reduce §
Defined as:
multi method reduce(Any: )multi sub reduce (, +list)
Returns a single "combined" value from a list of arbitrarily many values, by iteratively applying a routine which knows how to combine two values. In addition to the subroutine and the list, an initial value can be provided to initialize the reduction, which ends up being the return value if the list is empty. Thus reduce f, init, list
combines the elements of the list from left to right, as is shown in the following pseudocode:
result0 = init result1 = f(result0, list[0]) result2 = f(result1, list[1]) ... resultn = f(resultn-1, list[n-1])
resultn
is the final result for an n-element list.
say reduce :<+>, (1, 2, 3); # OUTPUT: «6» say (1, 2, 3).reduce: :<+>; # OUTPUT: «6» say reduce , (5, 9, 12, 1); # OUTPUT: «12»
If list
contains just a single element, the operator is applied to that single element if possible; if not, it returns the element itself.
say reduce :<->, (10,); # OUTPUT: «10»
When the list contains no elements, an exception is thrown, unless &with
is an operator with a known identity value (e.g., the identity value of infix:<+>
is 0). For this reason, you're advised to prefix the input list with an initial value (or explicit identity value):
my \strings = "One good string!", "And one another good string!";say reduce , '', |strings; # like strings.join my \numbers = 1, 2, 3, 4, 5;say reduce , 0, |numbers; # like numbers.max sub count-and-sum-evens( (Int \count, Int \sum), Int \x ) say reduce , (0, 0), |numbers; # OUTPUT: «(2 6)»
In the last example, since reduce
only supports one initial value we use a List
with two values, which is by itself a single value. The count-and-sum-evens
subroutine takes two positional values: a List
of two Int
s and an Int
, and return a List
storing the count and sum of the even integers accumulated.
If &with
is the code object of an operator, its inherent identity value and associativity is respected - in other words, (VAL1, VAL2, VAL3).reduce(&infix:<OP>)
is the same as VAL1 OP VAL2 OP VAL3
even for operators which aren't left-associative:
# Raise 2 to the 81st power, because 3 to the 4th power is 81 (2,3,4).reduce(:<**>).lsb.say; # OUTPUT: «81» (2**(3**4)).lsb.say; # OUTPUT: «81» (2**3**4).lsb.say; # OUTPUT: «81» # Subtract 4 from -1, because 2 minus 3 is -1 (2,3,4).reduce(:<->).say; # OUTPUT: «-5» ((2-3)-4).say; # OUTPUT: «-5» (2-3-4).say; # OUTPUT: «-5»
Since reducing with an infix operator is a common thing to do, the reduction metaoperator [ ]
provides a syntactic shortcut. Thus, instead of passing the operator's code object to reduce
, just pass the operator directly to [ ]
. To use a user-defined subroutine instead, provide an additional layer of square brackets around the subroutine's code object:
say [*] (1, 2, 3, 4); # OUTPUT: «24» say [min] (4, 2, 1, 3); # OUTPUT: «1» sub mult ;say [[]] (1, 2, 3, 4); # OUTPUT: «24»
Semantically, all the following do the same thing:
my \numbers = 1, 2, 3, 4, 5;say reduce , 0, |numbers;say reduce * + *, 0, |numbers;say reduce &[+], numbers; # operator does not need explicit identity value say [+] numbers;
Since reduce
is an implicit loop that iterates over with its reducing subroutine, it responds to next
, last
and redo
statements inside &with
:
sub last-after-seven ;say (2, 3, 4, 5).reduce: ; # OUTPUT: «9»
Whether reduce
accumulates the elements starting from the left or from the right depends on the operator. In the functional programming world, this operation is generally called a fold. With a right-associative operator it is a right fold, otherwise (and usually) it is a left fold. In Raku, you can specify the associativity of an operator with the is assoc
.
sub infix:<foo>(, ) is assoc<right> say [foo] 1, 2, 3, 4; # OUTPUT: «(1, (2, (3, 4)))» sub infix:<bar>(, ) is assoc<left> say [bar] 1, 2, 3, 4; # OUTPUT: «(((1, 2), 3), 4)»
Practical example 1: In this example, we generate a random-ish math formula (e.g., "(4 + ((3 * x) + 11) / 6))") using reduce
.
my = [Z] (<+ - * />, 1..20)».roll(4); say ('x', |).reduce: -> , [, ]
Practical example 2: Suppose we have a polynomial represented as a list of integer coefficients, c[n-1], c[n-2], ..., c[0], where c[i] is the coefficient of xi. We can evaluate it using map
and reduce
as follows:
sub evaluate(List \c where c.all ~~ Int, Rat \x --> Rat) my \c = 2, 3, 1; # 2x² + 3x + 1 say evaluate c, 3.0; # OUTPUT: «28» say evaluate c, 10.0; # OUTPUT: «231»