each_with_object应该如何工作?

时间:2013-09-28 06:48:19

标签: ruby

ruby​​的新手,我正在试图弄清楚应该如何使用each_with_object

我尝试将它用于一个非常简单的总和,所以我写道:

> (1..3).each_with_object(0) {|i,sum| sum+=i}
=> 0
嘿,我认为结果会是6!我的错误在哪里?

3 个答案:

答案 0 :(得分:51)

each_with_object不适用于整数等不可变对象。

(1..3).each_with_object(0) {|i,sum| sum += i} #=> 0

这是因为each_with_object遍历集合,将每个元素和给定对象传递给块。它不会在每次迭代后更新对象的值并返回原始给定对象。

它可以使用哈希,因为更改哈希键的值会自动更改原始对象的值。

(1..3).each_with_object({:sum => 0}) {|i,hsh| hsh[:sum] += i}
#=> {:sum => 6}

String个对象很有趣。它们是可变的,所以你可能期望以下 返回“abc”

("a".."c").each_with_object("") {|i,str| str += i} # => ""

但事实并非如此。这是因为str += "a"返回一个新对象,原始对象保持不变。但是,如果我们这样做

("a".."c").each_with_object("") {|i,str| str << i} # => "abc"

它有效,因为str << "a"修改了原始对象。

有关详细信息,请参阅each_with_object

的ruby文档

出于您的目的,请使用inject

(1..3).inject(0) {|sum,i| sum += i} #=> 6
# or
(1..3).inject(:+) #=> 6

答案 1 :(得分:10)

Enumerable#each_with_object的文档非常明确:

  

使用给定的任意对象迭代每个元素的给定块, 返回最初给定的对象

在您的情况下,(1..3).each_with_object(0) {|i,sum| sum+=i},您正在传递0,这是不可变对象。因此,each_with_object方法的初始对象是0,因此方法返回0。它在广告中起作用。请参阅下文each_with_object的工作原理,

(1..3).each_with_object(0) do |e,mem|
    p "#{mem} and #{e} before change"
    mem = mem + e
    p mem
end

# >> "0 and 1 before change"
# >> 1
# >> "0 and 2 before change"
# >> 2
# >> "0 and 3 before change"
# >> 3

这意味着在每次传递中,mem都设置为初始传递的对象。您可能会想到第一次传递mem0,然后在下一次传递中memmem+=e的结果,即。 mem将为1。但是,在每次传递mem中都是您的初始对象,即0

答案 2 :(得分:9)

使用each_with_object的一个简单但常见的示例是,您需要根据数组中的元素构建哈希。通常你会看到类似的东西:

hash = {}
[1, 2, 3, 4].each { |number| hash[number] = number**2 }
hash

使用each_with_object可以避免显式初始化并返回hash变量。

[1,2,3,4].each_with_object({}) { |number, hash| hash[number] = number**2 }

我建议您阅读injecttapeach_with_index的文档。当您寻求简短易读的代码时,这些方法很有用。