需要注入方法的简单说明

时间:2009-04-02 16:28:51

标签: ruby syntax

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

我正在看这段代码,但我的大脑没有记录数字10如何成为结果。有人会介意解释这里发生的事情吗?

17 个答案:

答案 0 :(得分:188)

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

在过程结束时,inject返回累加器,在这种情况下,累加器是数组中所有值的总和,或者10。

这是另一个从对象数组创建哈希的简单示例,以字符串表示为关键字:

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

在这种情况下,我们将累加器默认为空哈希,然后在每次执行块时填充它。请注意,我们必须将散列作为块的最后一行返回,因为块的结果将存储回累加器。

答案 1 :(得分:79)

inject接受一个值(示例中为0)和一个块,并为列表的每个元素运行该块一次。

  1. 在第一次迭代中,它传递您提供的值作为起始值,并传递列表的第一个元素,并保存块返回的值(在本例中为result + element)。
  2. 然后再次运行该块,将第一次迭代的结果作为第一个参数传递,将列表中的第二个元素作为第二个参数传递,再次保存结果。
  3. 它继续这种方式,直到它消耗了列表中的所有元素。
  4. 解释这一点的最简单方法可能是展示每个步骤的工作原理,例如;这是一组虚构的步骤,显示了如何评估此结果:

    [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
    

答案 2 :(得分:25)

inject方法的语法如下:

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

让我们解决上面的例子,即

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

10 作为输出。

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

result = 0 零来自注入(值)0

element = 1 它是数组的第一个元素。

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

步骤: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| }

这里 Bold-Italic 值是从数组中获取的元素,而简单的 Bold 值是结果值。

我希望您了解#inject的{​​{1}}方法的工作原理。

答案 3 :(得分:18)

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

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

答案 4 :(得分:14)

他们说了什么,但也要注意你并不总是需要提供“起始值”:

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

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

试试看,我会等。

当没有参数传递给inject时,第一个两个元素被传递给第一次迭代。在上面的示例中,结果为1,第一次元素为2,因此对该块的调用较少。

答案 5 :(得分:14)

你注入()中的数字代表起始位置,可以是0或1000。 管道内有两个占位符| x,y |。 x =你在.inject('x')里面的数字,而secound代表你对象的每次迭代。

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

  

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

答案 6 :(得分:6)

注入应用块

result + element

到数组中的每个项目。对于下一个项目(“元素”),从块返回的值是“result”。你调用它的方式(带参数),“result”以该参数的值开始。因此效果是添加元素。

答案 7 :(得分:6)

tldr; inject在一个重要方面与map不同:inject返回块上次执行的值,而map返回它迭代的数组。

超过每个块执行的值通过第一个参数(在本例中为result)传递到下一次执行,并且您可以初始化该值({{1}部分)。

上面的示例可以使用(0)编写,如下所示:

map

效果相同但result = 0 # initialize result [1, 2, 3, 4].map { |element| result += element } # result => 10 在这里更简洁。

您经常会在inject块中找到分配,而map块中会进行评估。

您选择哪种方法取决于inject所需的范围。何时使用它将是这样的:

result

你可能就像所有人一样,#34;看起来我,我只是将所有这一切合并为一行,"但是你也临时为result = [1, 2, 3, 4].inject(0) { |x, element| x + element } 分配了内存作为临时变量,因为你已经有x可以使用它。

答案 8 :(得分:4)

[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

答案 9 :(得分:3)

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

用简单的英语,你将经历(迭代)这个数组([1,2,3,4])。您将遍历此数组4次,因为有4个元素(1,2,3和4)。 inject方法有1个参数(数字0),你将该参数添加到第1个元素(0 + 1。这等于1)。 1保存在"结果"中。然后将结果(即1)添加到下一个元素(1 + 2。这是3)。 现在将保存为结果。继续前进:3 + 3等于6.最后,6 + 4等于10.

答案 10 :(得分: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

答案 11 :(得分:1)

从这里开始,然后查看所有采用块的方法。 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 }

在元素检查器块中。

答案 12 :(得分:0)

还有另一种形式的.inject()方法非常有用 [4,5] .inject(&:+)这将加上区域的所有元素

答案 13 :(得分:0)

如果您熟悉其他语言,那只是reducefold

答案 14 :(得分:0)

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

忘记“初始值”,因为它在开始时有些混乱。

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

您可以将上面的理解为:我正在1,2,3,4之间注入“添加机”。意思是,它是1 and 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

答案 15 :(得分:0)

有一天,我也对 Ruby 注入/减少方法中的默认值感到震惊,所以我尝试visualize my issue

default values vizualized

答案 16 :(得分:-1)

与此相同:

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