routine reduce

Documentation for routine reduce assembled from the following types:

class Supply

From Supply

(Supply) method reduce

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

Creates a "reducing" supply 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 Any

From Any

(Any) method reduce

Defined as:

multi method reduce(Any:U: & --> Nil)
multi method reduce(Any:D: &with)
multi sub reduce (&with+list)

Applying it to a class will always produce Nil. Applies its argument (or first argument, in case it's a sub) as an operator to all the elements in the object (or second argument), producing a single result. The argument must be an infix operator or take, in any case, two positional arguments.

(1..13).reduce( &[*] ).say# OUTPUT: «6227020800␤»

class List

From List

(List) routine reduce

Defined as:

multi method reduce(Any:U: & --> Nil)
multi method reduce(Any:D: &with)
multi sub reduce (&with+list)

The first form is obviously a no-op. The second form generates a single "combined" value from a list of arbitrarily many values, by iteratively applying a function which knows how to combine two values.

If @values contains just a single element, the operator is applied to that single element if possible; if not, it returns the element itself.

say [-] <10 5 3>#OUTPUT: 2␤ 
say [-] 10;       #OUTPUT: 10␤

If it contains no elements, an exception is thrown, unless &with is an operator with a known identity value. For this reason, you may want to prefix the input list with an explicit identity value:

my @strings = ("One good string!""And one another good string!");
say reduce { $^a ~ $^b }''|@strings;               # like @strings.join 
my @numbers = (1,2,3,4,5);
say reduce { $^a > $^b ?? $^a !! $^b }0|@numbers;  # like @numbers.max

If &with is the function object of an operator, its inherent identity value and associativity is respected - in other words, (VAL1, VAL2, VAL3).reduce(&[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 [ ] metaoperator provides a syntactic shortcut:

# The following all do the same thing... 
my @numbers = (1,2,3,4,5);
say reduce { $^a + $^b }0|@numbers;
say reduce * + *0|@numbers;
say reduce &[+], @numbers# operator does not need explicit identity 
say [+@numbers;          # most people write it this way

Please note also the use of reduce in sub form. Since reduce is an implicit loop, it responds to next, last and redo statements inside &with:

say (2,3,4,5).reduce: { last if $^a > 7$^a + $^b }# says 9

Practical example:

# Generate a random-ish math formula like "(4 + ((3 * x) + 11) / 6))" 
 
my @ops = [Z] (<+ - * />1..20.roll(4);
 
say ('x'|@ops).reduce: -> $formula, [$op$number{
    Bool.pick ?? "($formula $op $number)"
              !! "($number $op $formula)"
}

Note: 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:

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)␤»