我希望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,那么可以确信函数不会弄乱它(好吧,它可以是可变的,但你明白了。)
答案 0 :(得分:3)
a = [[1, 2, 3], [10, 20], ["a"]]
a.each { |x| x[0]=5 }
在上面的示例中,x
是array
(您将在每次迭代中传递给块),您从该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)
使用each
或map
不会更改数组本身。但是看起来它可能会改变数组中的元素。实际上,当一个数组持有对其他对象的引用时,引用保持不变,但引用的对象本身可以改变。我同意你学习它时会感到惊讶。
你注意到了什么:
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']
我建议阅读有关诸如引用对象,指针,按值调用和按引用调用的主题的内容。