`each_with_object`的块内的`+ =`和`<<`之间的区别

时间:2016-02-09 13:45:50

标签: arrays ruby iterator immutability

我必须更新数组,并且在传递给+=的块内的不同代码行中使用了<<Array#each_with_object

代码1

(1..5).each_with_object([]) do |i, a|
  puts a.inspect
  a += [i]
end

输出:

[]
[]
[]
[]
[]

代码2

(1..5).each_with_object([]) do |i, a|
  puts a.inspect
  a << [i]
end

输出:

[]
[1]
[1,2]
[1,2,3]
[1,2,3,4]

+=运算符不会更新原始数组。为什么?我在这里缺少什么?

2 个答案:

答案 0 :(得分:4)

each_with_object中,所谓的备忘录对象在迭代中很常见。您需要修改该对象才能执行有意义的操作。 +=运算符是+和赋值的语法糖,它不会修改接收者,因此迭代无效。如果您使用<<push等方法,则会生效。

另一方面,在inject中,所谓的memo对象是块的返回值,你不需要修改对象,但是你需要返回值想要下一次迭代。

答案 1 :(得分:4)

  

我很清楚+ =运算符没有更新原始数组。为什么呢?

因为the documentation这样说(强调我的):

  

ary + other_arynew_ary

     

连接 - 返回通过将两个数组连接在一起以生成第三个数组而构建的 new 数组。

[ 1, 2, 3 ] + [ 4, 5 ]    #=> [ 1, 2, 3, 4, 5 ]
a = [ "a", "b", "c" ]
c = a + [ "d", "e", "f" ]
c                         #=> [ "a", "b", "c", "d", "e", "f" ]
a                         #=> [ "a", "b", "c" ]
     

请注意

x += y
     

相同
x = x + y
     

这意味着它会生成 new 数组。因此,在数组上重复使用+=可能效率很低。

     

另见#concat

<<

比较
  

ary << objary

     

追加 - 将给定对象推送到数组的末尾。该表达式返回数组本身,因此可以将多个附加链接在一起。

[ 1, 2 ] << "c" << "d" << [ 3, 4 ]
        #=>  [ 1, 2, "c", "d", [ 3, 4 ] ]

Array#+的文档清楚地表明返回了 new 数组(实际上不少于4次)。这与Ruby中+方法的其他用法一致,例如: Bignum#+Fixnum#+Complex#+Rational#+Float#+Time#+String#+BigDecimal#+,{{ 3}},Date#+Matrix#+Vector#+Pathname#+Set#+