Documentation for routine reduce assembled from the following pages:

Class: Any §

From Any

(Any) routine reduce §

Defined as:

multi method reduce(Any:U: & --> Nil)
multi method reduce(Any:D: &with)
multi sub reduce (&with+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(&infix:<+>);   # OUTPUT: «10␤» 
say reduce &infix:<+>1..4;     # OUTPUT: «10␤» 
say reduce &min1..4;           # OUTPUT: «1␤» 
 
sub hyphenate(Str \aStr \b{ a ~ '-' ~ b }
say reduce &hyphenate'a'..'c'# OUTPUT: «a-b-c␤»

Applied to a class, the routine will always return Nil.

say Range.reduce(&infix:<+>);    # OUTPUT: «Nil␤» 
say Str.reduce(&infix:<~>);      # OUTPUT: «Nil␤»

See List.reduce for a more thorough discussion.

Class: Supply §

From Supply

(Supply) method reduce §

Defined as:

method reduce(Supply:D: &with --> Supply:D)

Creates a "reducing" supply, which will emit a single value with the same semantics as List.reduce.

my $supply = Supply.from-list(1..5).reduce({$^a + $^b});
$supply.tap(-> $v { say "$v" }); # OUTPUT: «15␤»

Class: List §

From List

(List) routine reduce §

Defined as:

multi method reduce(Any:D: &with)
multi sub reduce (&with+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 &infix:<+>, (123); # OUTPUT: «6␤» 
say (123).reduce: &infix:<+># OUTPUT: «6␤» 
say reduce &max, (59121);   # 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 &infix:<->, (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 { $^a ~ $^b }''|strings;               # like strings.join 
 
my \numbers = 12345;
say reduce { $^a > $^b ?? $^a !! $^b }0|numbers# like numbers.max 
 
sub count-and-sum-evens( (Int \countInt \sum), Int \x ) {
    x %% 2 ?? (count+1, sum+x!! (countsum)
}
 
say reduce &count-and-sum-evens, (00), |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 Ints 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(&infix:<**>).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(&infix:<->).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 [*] (1234);       # OUTPUT: «24␤» 
say [min] (4213);     # OUTPUT: «1␤» 
 
sub mult { $^a * $^b };
say [[&mult]] (1234); # OUTPUT: «24␤»

Semantically, all the following do the same thing:

my \numbers = 12345;
say reduce { $^a + $^b }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 { last if $^a > 7$^a + $^b };
say (2345).reduce: &last-after-seven# 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>($a$bis assoc<right> { "($a$b)" }
say [foo1234# OUTPUT: «(1, (2, (3, 4)))␤» 
 
sub infix:<bar>($a$bis assoc<left> { "($a$b)" }
say [bar1234# 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 @ops = [Z] (<+ - * />1..20.roll(4);
 
say ('x'|@ops).reduce: -> $formula, [$op$number{
    Bool.pick ?? "($formula $op $number)"
              !! "($number $op $formula)"
}

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:D \c where c.all ~~ IntRat:D \x --> Rat:D{
    my \xi = (c.elems ^... 0).map: -> \i { x ** i }# [x^(n-1), ..., x^0] 
    my \axi = [+c Z* xi;                           # [c[n-1]*x^(n-1), ..., c[*]x^0] 
    [+axi;                                         # sum of axi 
}
 
my \c = 231;       # 2x² + 3x + 1 
say evaluate c3.0;   # OUTPUT: «28␤» 
say evaluate c10.0;  # OUTPUT: «231␤»