【问题标题】:How is each_with_object supposed to work?each_with_object 应该如何工作?
【发布时间】:2013-09-28 06:48:19
【问题描述】:

我试图弄清楚应该如何使用each_with_object

我有一个不起作用的求和示例:

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

我会假设结果是 6 !我的错在哪里?

【问题讨论】:

  • 顺便说一句。有人可以告诉我为什么 Ruby 在执行sum+=i 时没有抛出某种错误,即。试图修改不可变对象?这里的重点是可怜的用户被单独留下,不知道他的命令只是默默地失败了......

标签: ruby


【解决方案1】:

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

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

这是因为each_with_object 遍历集合,将每个元素和给定对象传递给块。它不会在每次迭代后更新 object 的值,而是返回原始给定的 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 &lt;&lt; "a" 修改了原始对象。

有关更多信息,请参阅each_with_object 的 ruby​​ 文档

为了您的目的,请使用inject

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

【讨论】:

  • 好的,谢谢。确实,我在这里阅读文档:ruby-doc.org/core-2.0.0/… 并且没有提及此类细节。谢谢。
  • 问题是sum+=isum = sum + isum = ... 不会改变传入的sum,它只会为局部变量分配一个新值。 inject 有效,因为 inject 将块的返回值反馈到下一次迭代中,在这种情况下,您可能最好写 (1..3).inject(0) {|sum,i| sum + i } 甚至 (1..3).inject(:+)sum += iinject 块内会产生误导和混乱。
  • 我更喜欢sum+i,因为它更清晰,但each_with_object 不适用。我想证明代码 OP 可以与 inject 一起使用,但不能与 each_with_object 一起使用
  • 但整个方法不适用于each_with_object,因为正如您所指出的,Fixnums 是不可变的。 each_with_object 是这个工作的错误工具。
  • 这是一个很好的解决方案,这些示例应该添加到 ruby​​ 文档中!
【解决方案2】:

使用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 的文档。当您的目标是简短易读的代码时,这些方法很有帮助。

【讨论】:

  • 是的,三年半前我就是这样想的 ;-)
【解决方案3】:

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

【讨论】:

  • 好的,我刚刚知道整数是不可变的,我不知道。谢谢。
猜你喜欢
  • 2023-03-03
  • 2016-02-16
  • 2010-10-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-31
  • 2017-01-14
相关资源
最近更新 更多