每个Ruby并收集更改数组的数组

时间:2014-03-29 20:56:43

标签: ruby

我希望Array.each和Array.collect永远不会改变对象,就像在这个例子中一样:

a = [1, 2, 3]
a.each { |x| x = 5 }
a #output => [1, 2, 3]

但是当您使用数组数组或哈希数组时,情况似乎并非如此:

a = [[1, 2, 3], [10, 20], ["a"]]
a.each { |x| x[0]=5 }
a #output => [[5, 2, 3], [5, 20], [5]]

这种行为是期待还是我做错了什么?

这不会让红宝石行为出乎意料吗?例如,在C ++中,如果函数参数被声明为const,那么可以确信函数不会弄乱它(好吧,它可以是可变的,但你明白了。)

2 个答案:

答案 0 :(得分:3)

a = [[1, 2, 3], [10, 20], ["a"]]
a.each { |x| x[0]=5 }

在上面的示例中,xarray(您将在每次迭代中传递给块),您从该0th索引中访问该元素,并且更新它。由于数组是可变对象,它也会更新。这里a是一个数组数组。

1 st 迭代中,x[1, 2, 3]。现在,您调用Array#[]=方法来更新[1, 2, 3] 0 th 元素。

2 nd 迭代x[10, 20]。与上述相同。

..依此类推。因此,#each完成迭代后,您修改了a

a = [1, 2, 3]
a.each { |x| x = 5 }

在上面的示例中,您将数组元素传递给每个块,它们是 Fixnum 对象,并且也不可变。这里a是一个元素数组,你只是访问这些元素。

更新(清除OP' comment

a = [[1, 2, 3], [10, 20], ["a"]]
a.each do |x|
  # here x is holding the object from the source array `a`.
  x # => [1, 2, 3]
  x.object_id # => 72635790
  # here you assgined a new array object, which has the same content as the
  # inner array element [1, 2, 3]. But strictly these are 2 different object. Check
  # out the object_id of those two.
  x = [1, 2, 3]
  x # => [1, 2, 3]
  x.object_id # => 72635250
  break # used break to stop iteration after 1st one.
end

答案 1 :(得分:1)

使用eachmap不会更改数组本身。但是看起来它可能会改变数组中的元素。实际上,当一个数组持有对其他对象的引用时,引用保持不变,但引用的对象本身可以改变。我同意你学习它时会感到惊讶。

你注意到了什么:

a = ['a', 'b', 'c']
a.each { |x| x[0] = 'x' }
puts a    # => ['x', 'x', 'x']

这里第一个数组元素仍引用相同的字符串,但字符串已更改。

为什么理解这些参考文献很重要?

array = ['a', 'b', 'c']
a = array
b = array
puts b # => ['a', 'b', 'c']
a[0] = 'x'
puts b # => ['x', 'b', 'c']

freeze是否可以保护我们免受更改?

a = ['a', 'b', 'c'].freeze
a << ['d'] # throws 'can't modify frozen Array (RuntimeError)'

似乎是这样。但同样只针对阵列本身。它没有深度冻结阵列。

a[0][0] = 'x'
puts a.inspect ['x', 'b', 'c']

我建议阅读有关诸如引用对象,指针,按值调用和按引用调用的主题的内容。