Need a simple explanation of the inject method

[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10

I'm looking at this code but my brain is not registering how the number 10 can become the result. Would someone mind explaining what's happening here?

70441 次浏览

可以将第一个块参数视为累加器: 块的每次运行的结果存储在累加器中,然后传递给块的下一次执行。对于上面显示的代码,您将累加器 result 默认设置为0。该块的每次运行都将给定的数字添加到当前的总数中,然后将结果存储回累加器中。下一个块调用具有这个新值,添加它,再次存储它,然后重复。

在进程结束时,注入返回累加器,在本例中,累加器是数组中所有值的和,或者10。

下面是另一个从对象数组中创建散列的简单示例,该散列由对象的字符串表示形式键控:

[1,"a",Object.new,:hi].inject({}) do |hash, item|
hash[item.to_s] = item
hash
end

在本例中,我们将累加器默认为空散列,然后在每次执行块时填充它。注意,我们必须将 hash 作为块的最后一行返回,因为块的结果将存储回累加器中。

代码迭代数组中的四个元素,并将前一个结果添加到当前元素:

  • 1 + 2 = 3
  • 3 + 3 = 6
  • 6 + 4 = 10

注入将应用该块

result + element

到数组中的每个项。对于下一个项目(“ element”) ,从块返回的值是“ result”。按照您调用它的方式(使用一个参数) ,“ result”以该参数的值开始。所以效果就是把元素加起来。

inject以一个值(例子中的 0)和一个块开始,它对列表中的每个元素运行该块一次。

  1. 在第一次迭代时,它将提供的值作为起始值和列表的第一个元素传入,并保存块返回的值(在本例中为 result + element)。
  2. 然后再次运行该块,将第一次迭代的结果作为第一个参数传入,将列表中的第二个元素作为第二个参数传入,再次保存结果。
  3. 它以这种方式继续,直到它使用完列表的所有元素。

对于你的例子来说,解释这个问题最简单的方法可能是展示每个步骤是如何工作的; 这是一组假想的步骤,展示了如何评估这个结果:

[1, 2, 3, 4].inject(0) { |result, element| result + element }
[2, 3, 4].inject(0 + 1) { |result, element| result + element }
[3, 4].inject((0 + 1) + 2) { |result, element| result + element }
[4].inject(((0 + 1) + 2) + 3) { |result, element| result + element }
[].inject((((0 + 1) + 2) + 3) + 4) { |result, element| result + element }
(((0 + 1) + 2) + 3) + 4
10

正如他们所说,但请注意,你并不总是需要提供一个“起始值”:

[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10

[1, 2, 3, 4].inject { |result, element| result + element } # => 10

试试看,我等你。

如果没有传入要注入的参数,则将第一个 元素传入第一次迭代。在上面的示例中,结果为1,第一次元素为2,因此对块的调用减少了一次。

这段代码不允许不传递起始值的可能性,但是可以帮助解释发生了什么。

def incomplete_inject(enumerable, result)
enumerable.each do |item|
result = yield(result, item)
end
result
end


incomplete_inject([1,2,3,4], 0) {|result, item| result + item} # => 10

您在注入()中输入的数字代表一个起始位置,它可以是0或1000。 在管道内部有两个位置固定器 | x,y | 。X = 你在。注入(‘ x’) ,并且秒表示对象的每次迭代。

[1, 2, 3, 4].inject(5) { |result, element| result + element } # => 15

1 + 5 = 6 2 + 6 = 8 3 + 8 = 11 11 + 4 = 15

和这个是一样的:

[1,2,3,4].inject(:+)
=> 10
[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10

等同于:

def my_function(r, e)
r+e
end


a = [1, 2, 3, 4]
result = 0


a.each do |value|
result = my_function(result, value)
end

injectmap在一个重要方面有所不同: inject返回块的最后一次执行的值,而 map返回它迭代过的数组。

大于 每个块执行的值通过第一个参数(本例中为 result)传递给下一个执行,您可以初始化该值((0)部分)。

上面的例子可以像这样使用 map编写:

result = 0 # initialize result
[1, 2, 3, 4].map { |element| result += element }
# result => 10

同样的效果,但 inject在这里更简洁。

您通常会发现,赋值发生在 map块中,而求值发生在 inject块中。

选择哪种方法取决于您想要用于 result的范围。什么时候使用 没有应该是这样的:

result = [1, 2, 3, 4].inject(0) { |x, element| x + element }

您可能会说,“看,我只是把它们合并到一行中”,但是您还需要临时为 x分配内存作为一个划伤变量,这是不必要的,因为您已经使用了 result

[1, 2, 3, 4].inject(0) { |result, element| result + element } # => 10

在简单的英语中,您将遍历(迭代)这个数组([1,2,3,4])。您将遍历这个数组4次,因为有4个元素(1、2、3和4)。注入方法有1个参数(数字0) ,您将把该参数添加到第1个元素(0 + 1)。这等于1)。1保存在“ result”中。然后将结果(即1)添加到下一个元素(1 + 2)。这是3)。这个现在将作为结果保存。继续: 3 + 3等于6。最后,6 + 4等于10。

注入方法的语法如下:

inject (value_initial) { |result_memo, object| block }

让我们解决上面的例子。

[1, 2, 3, 4].inject(0) { |result, element| result + element }

它给出 10作为输出。

因此,在开始之前,让我们看看存储在每个变量中的值是什么:

Result = 0 0来自 < em > 注入(value) ,其值为0

Element = 1 It 是数组的第一个元素。

好的! ! ! 那么,让我们开始理解上面的例子

步骤: 1 [1, 2, 3, 4].inject(0) { |0, 1| 0 + 1 }

步骤: 2 [1, 2, 3, 4].inject(0) { |1, 2| 1 + 2 }

步骤: 3 [1, 2, 3, 4].inject(0) { |3, 3| 3 + 3 }

步骤: 4 [1, 2, 3, 4].inject(0) { |6, 4| 6 + 4 }

步骤: 5 [1, 2, 3, 4].inject(0) { |10, Now no elements left in the array, so it'll return 10 from this step| }

这里的 黑体斜体值是从数组中获取的元素,简单的 大胆值是结果值。

我希望您能够理解 #ruby#inject方法的工作原理。

从这里开始,然后检查所有采用块的方法。 Http://ruby-doc.org/core-2.3.3/enumerable.html#method-i-inject

是块让你感到困惑还是你为什么在方法中有一个值? 问得好,这里的操作符方法是什么?

result.+

一开始是什么?

#inject(0)

我们能这么做吗?

[1, 2, 3, 4].inject(0) { |result, element| result.+ element }

这个有用吗?

[1, 2, 3, 4].inject() { |result = 0, element| result.+ element }

我建立在这样一个想法上它只是对数组中的所有元素求和然后在文档中看到的备忘录中得到一个数字。

你随时都可以这么做

 [1, 2, 3, 4].each { |element| p element }

查看数组的可枚举数的迭代过程,这是基本思想。

它只是注入或减少给你一个备忘录或累加器,被发送出去。

我们可以尝试得到一个结果

[1, 2, 3, 4].each { |result = 0, element| result + element }

但是没有任何结果,所以这个结果和之前一样

[1, 2, 3, 4].each { |result = 0, element| p result + element }

在元素检查区。

还有另一种形式的. register ()方法,非常有用 注入(& : +) ,它会把这个区域的所有元素加起来

这是一个简单易懂的解释:

忘记“初始值”吧,因为它在一开始有些令人困惑。

> [1,2,3,4].inject{|a,b| a+b}
=> 10

你可以理解为: 我在1,2,3,4之间注入了一个“加法机器”。意思是,它是1? 2? 3? 4,而且是一个加法器,所以它和1 + 2 + 3 + 4是一样的,它是10。

你实际上可以在它们之间注入一个 +:

> [1,2,3,4].inject(:+)
=> 10

就像是在1,2,3,4之间注入一个 +使它成为1 + 2 + 3 + 4它就是10。:+是 Ruby 以符号的形式指定 +的方式。

这很容易理解和直观。如果你想逐步分析它是如何工作的,就像这样: 取1和2,现在加上它们,当你有一个结果,首先存储它(3) ,现在,接下来是存储值3和数组元素3通过 a + b 过程,这是6,现在存储这个值,现在6和4通过 a + b 过程,是10。你实际上是在做

((1 + 2) + 3) + 4

是10。“初始值”0只是一个开始的“基数”。在许多情况下,你并不需要它。想象一下,如果你需要1 * 2 * 3 * 4,它就是

[1,2,3,4].inject(:*)
=> 24

就这么定了。你不需要一个“初始值”的 1乘以整个事情与 1。这一次,它做到了

(((1 * 2) * 3) * 4)

你得到的结果和

1 * 2 * 3 * 4

有一天,我也对 Ruby 注入/减少方法中的默认值感到头疼,所以我尝试了 想象一下我的问题:

default values vizualized

当使用任何类型的集合时,出现的一个常见场景是使用所有元素执行单一类型的操作并收集结果。

例如,sum (array)函数可能希望添加作为数组传递的所有元素并返回结果。

Ruby 以 reduce (注入是一个别名)的名义提供了相同功能的广义抽象。也就是说,这些方法迭代集合,并使用运算符累积基值中元素的操作值,最终返回该基值。

让我们举个例子来更好地理解。

>>> (5..10).inject(1) {|product, n| product * n }
=> 151200

在上面的示例中,我们有以下元素: 基值1、可枚举(5。.10) ,以及一个包含表达式的块,指示如何将计算值添加到基值(即,将数组元素乘以 product,其中 product 用基值初始化)

所以执行过程是这样的:

# loop 1
n = 1
product = 1
return value = 1*1


# loop 2
n = 2
product = 1
return value = 1*2


n = 3
product = 2
return value = 2*3

.. 正如您可以注意到的,当表达式循环通过容器元素时,基值会不断更新,从而返回基值的最终值作为结果。