Ruby中reduce/inject方法的更深入解释

6

我已经思考了一段时间,想了很多方法。

使用reduce时,为什么第一个元素会在执行块中定义的操作之前返回?或者我是否在reduce的工作方式上漏掉了关键点?

在下面的示例中:

arr = [1, 3, 5]

arr.reduce {|sum, n| sum + (n * 3) }
#=> 25

我本以为结果应该是27
因为:
0 + (1 * 3) = 3
3 + (3 * 3) = 12
12 + (5 * 3) = 27

经过一段时间的尝试,我发现在第一个“tick”中,数组中的对象仅被加到总和中而不是被乘。因此,计算更像是:

??? = 1
1 + (3 * 3) = 10
10 + (5 * 3) = 25

能否有人帮我找出我走错的路在哪里?

这段内容与IT技术无关。

4
[1, 3, 5].reduce(0) { |sum, n| sum + n * 3 } 是你想要的 - 它使用初始值为 0(或者你传递的任何值)因此返回 27 而不是 25 - Stefan
2
在你的第一个方程中,零是从哪里来的?它不在数组里吗? - Jörg W Mittag
1
@JörgWMittag - 我本来期望 sum 的初始值默认为零。 - Bergrebell
2
想象一下,如果你想要计算阶乘。乘法的中性元素是1,而不是0。或者,如果你想要构建一些字符串。字符串连接的中性元素是空字符串。或者,你想要构建一个哈希表;哈希合并的中性元素是空哈希表。为了选择正确的中性元素,Ruby必须读取你的思想,以便知道你想要计算什么!如果你这样考虑,就应该清楚为什么元素只能来自数组。 - Jörg W Mittag
3个回答

7

文档中有相关内容。

如果您没有显式指定memo的初始值,则使用集合的第一个元素作为memo的初始值。


Peter,初始备忘录非常重要 - 特别是当您尝试在没有初始备忘录的情况下reduce一个空数组时会发生什么... - Mulan

7

我曾遇到过Ruby注入/缩减方法中默认值的类似问题,所以我尝试可视化它:

default values vizualized


1
我已经明白了,但那是一个非常好的解释! - Bergrebell
1
是的,这已经有一段时间了,但希望这可以避免一些人在这里碰巧遇到它时进行大脑运动 :) 感谢您接受它作为答案! - Rich Steinmetz

2

我认为在这种情况下,方法的帮助可以解释问题:

[1] pry(main)> cd Array
[2] pry(Array):1> ? reduce

From: enum.c (C Method):
Owner: Enumerable
Visibility: public
Signature: reduce(*arg1)
Number of lines: 33

Combines all elements of enum by applying a binary
operation, specified by a block or a symbol that names a
method or operator.

The inject and reduce methods are aliases. There
is no performance benefit to either.

If you specify a block, then for each element in enum
the block is passed an accumulator value (memo) and the element.
If you specify a symbol instead, then each element in the collection
will be passed to the named method of memo.
In either case, the result becomes the new value for memo.
At the end of the iteration, the final value of memo is the
return value for the method.

If you do not explicitly specify an initial value for memo,
then the first element of collection is used as the initial value
of memo.


   # Sum some numbers
   (5..10).reduce(:+)                             #=> 45
   # Same using a block and inject
   (5..10).inject { |sum, n| sum + n }            #=> 45
   # Multiply some numbers
   (5..10).reduce(1, :*)                          #=> 151200
   # Same using a block
   (5..10).inject(1) { |product, n| product * n } #=> 151200
   # find the longest word
   longest = %w{ cat sheep bear }.inject do |memo, word|
      memo.length > word.length ? memo : word
   end
   longest                                        #=> "sheep"

所以您需要将第一个备忘录指定为0,在您的情况下是1:
[4] pry(Array):1> [1,3,5].reduce(0) {|sum, n| sum + (n * 3) }
=> 27

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接