操作数组会反映在引用它的所有变量中

时间:2012-10-07 08:24:00

标签: ruby mutable

如果我这样做

x = y = z = 1
z = 20

然后我得到

"#{x}----#{y}----#{z}"
# => "1----1----20" 

现在,如果我做这样的事情:

a = b = c = []

然后我明白了:

"#{a}-----#{b}-----#{c}"
# => "[]-----[]-----[]"

但如果我这样做:

c[0] = 'a'
c[1] = 'b'
c[2] = 'c'

我明白了:

"#{a}-----#{b}-----#{c}"
# => "[\"a\", \"b\", \"c\"]-----[\"a\", \"b\", \"c\"]-----[\"a\", \"b\", \"c\"]"

如果是xyz,当我将z分配给20时,xy保留了值1。对于Arrays,即使我仅将值分配给c[],更改也会反映在ab中。 Array s发生了什么?

3 个答案:

答案 0 :(得分:1)

使用z = 20,您将更改z以引用其他对象

使用c[0] = 'a',您正在更改ab也引用的原始对象。

答案 1 :(得分:1)

考虑将哪些对象分配给哪些变量,毕竟变​​量只是对象的句柄。在ruby中,一切都是一个对象,变量就是你与它们交互的方式。

因此...

a = b = 1

这会将ab设置为引用同一个对象。如果我后来说:

a = 2

然后我设置a来引用一个新对象,它根本不会影响b,这仍然是1。该插件只需一个简单的步骤。

  1. 将局部变量a设置为对象2
  2. 但是,遵循这个逻辑:

    a = b = []
    

    同样,ab引用同一个对象。这次是阵列。但现在我们这样做了:

    a[0] = 'hello'
    

    我们这里有不同的情况。您根本没有更改对象a引用。您正在查找对象a引用,然后修改该对象。

    考虑一下口译员会做什么。执行该行时将执行以下步骤。

    1. 找到a引用的对象(恰好是b引用的同一对象)
    2. 将该对象的0索引处的值设置为字符串"hello"
    3. 这就是说简单的局部变量赋值如:

      a = 1
      

      当存在如下所示的间接层时,操作会有所不同:

      a[0] = 'hello'
      a.foo = 'bar'
      a.set_value 'some val'
      

      或者考虑在数组中设置一个值,就像调用该数组上的方法一样。因此,如果您将a[0] = 'foo'视为:

      ,则差异会变得更容易理解
      a.set_value(0, 'foo')
      

      这样的事实上是ruby数组中发生的事情。结果为数组索引赋值调用[]=(index, value)方法。这些都是有效的和等效的。

      a[0] = 'foo'
      a.[]=(0, 'foo')
      a.send('[]=', 0, 'foo')
      

      我指出这一点只是因为当表达为方法的调用时,我们很清楚我们在这里修改现有对象。

答案 2 :(得分:0)

在Ruby中,你有一些方法可以改变它返回新对象的对象和方法(方法的文档会提到这一点)。整数不能变异:两个永远是两个。使用object_id可能会让事情变得清晰:

puts 1.object_id # 3
a = 1
puts a.object_id # 3
puts 20.object_id # 41
a = 20
puts a.object_id # 41

b = []
puts b.object_id # a number
b[1]=1 # mutating object
puts b.object_id # same number
b = b+[2] # the + method results in new object http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-2B
puts b.object_id # another number