在列表中§

有关例程 reduce 的主要文档,请参阅上下文

multi method reduce(Any:D: &with)
multi        reduce (&with+list)

通过迭代应用知道如何组合两个值的例程,从任意多个值的列表中返回单个“组合”值。除了子例程和列表之外,还可以提供初始值来初始化 reduce,如果列表为空,该值最终将成为返回值。因此,reduce f, init, list 从左到右组合列表的元素,如下面的伪代码所示

result0 = init
result1 = f(result0, list[0])
result2 = f(result1, list[1])
...
resultn = f(resultn-1, list[n-1])

resultn 是 n 元素列表的最终结果。

say reduce &infix:<+>, (123); # OUTPUT: «6␤» 
say (123).reduce: &infix:<+># OUTPUT: «6␤» 
say reduce &max, (59121);   # OUTPUT: «12␤»

如果 list 仅包含单个元素,则运算符将应用于该单个元素(如果可能);否则,它将返回元素本身。

say reduce &infix:<->, (10,);     # OUTPUT: «10␤»

当列表不包含任何元素时,将引发异常,除非 &with 是一个具有已知标识值的_运算符_(例如,infix:<+> 的标识值为 0)。因此,建议您在输入列表前添加初始值(或显式标识值)。

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

在最后一个示例中,由于 reduce 仅支持一个初始值,因此我们使用包含两个值的 List,它本身就是一个值。 count-and-sum-evens 子例程接受两个位置值:一个包含两个 IntList 和一个 Int,并返回一个存储累积的偶数整数的计数和总和的 List

如果 &with 是_运算符_的代码对象,则将遵循其固有的标识值和结合性 - 换句话说,(VAL1, VAL2, VAL3).reduce(&infix:<OP>)VAL1 OP VAL2 OP VAL3 相同,即使对于非左结合运算符也是如此。

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

由于使用中缀运算符进行 reduce 是一种常见操作,因此 reduce 元运算符 [ ] 提供了一种语法快捷方式。因此,无需将运算符的代码对象传递给 reduce,只需将运算符直接传递给 [ ]。要改用用户定义的子例程,请在子例程的代码对象周围再加一层方括号。

say [*] (1234);       # OUTPUT: «24␤» 
say [min] (4213);     # OUTPUT: «1␤» 
 
sub mult { $^a * $^b };
say [[&mult]] (1234); # OUTPUT: «24␤»

从语义上讲,以下所有操作都执行相同的操作。

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;

由于 reduce 是一个隐式循环,它使用其_reduce_子例程进行迭代,因此它会响应 &with 中的 nextlastredo 语句。

sub last-after-seven { last if $^a > 7$^a + $^b };
say (2345).reduce: &last-after-seven# OUTPUT: «9␤»

reduce 是从左到右还是从右到左累积元素取决于运算符。在函数式编程领域,此操作通常称为 fold。对于右结合运算符,它是_右折叠_,否则(通常)它是_左折叠_。在 Raku 中,您可以使用 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)␤»

**实际示例 1:** 在此示例中,我们使用 reduce 生成一个随机数学公式(例如,“(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)"
}

**实际示例 2:** 假设我们有一个多项式,表示为整数系数列表 c[n-1]、c[n-2]、...、c[0],其中 c[i] 是 xi 的系数。我们可以使用 mapreduce 对其进行如下评估。

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

在 Supply 中§

有关 method reduce 的主要文档,请参阅上下文

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

创建一个“reduce”供应,它将发出具有与 List.reduce 相同语义的单个值。

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

在 Any 中§

有关 routine reduce 的主要文档,请参阅上下文

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

此例程通过应用二元子例程组合列表对象中的元素,并生成单个结果。它将其参数(或子例程形式的第一个参数)作为运算符应用于对象中的所有元素(或子例程形式的第二个参数),从而生成单个结果。子例程必须是 中缀运算符 或接受两个位置参数。使用中缀运算符时,我们必须提供其子例程版本的代码对象,即运算符类别,后跟冒号,然后是带有构成运算符的符号的列表引用结构(例如,infix:<+>)。请参阅运算符

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

应用于类时,例程将始终返回 Nil

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

有关更详细的讨论,请参阅 List.reduce