【发布时间】:2013-08-12 08:24:06
【问题描述】:
例如:
a = [1,2,3,4]
b = a
c = a.to_a
a.insert(0,0) #=> [0,1,2,3,4]
b #=> [0,1,2,3,4]
c #=> [0,1,2,3,4]
为什么数组b和c的输出是一样的?如果我想获取数组a 的副本,而不是引用,我应该使用哪种方法?
【问题讨论】:
例如:
a = [1,2,3,4]
b = a
c = a.to_a
a.insert(0,0) #=> [0,1,2,3,4]
b #=> [0,1,2,3,4]
c #=> [0,1,2,3,4]
为什么数组b和c的输出是一样的?如果我想获取数组a 的副本,而不是引用,我应该使用哪种方法?
【问题讨论】:
为什么数组b和c的输出是一样的?
因为三个局部变量都引用了同一个对象,如下:
a = [1,2,3,4]
b = a
c = a.to_a
a.object_id
# => 72187200
b.object_id
# => 72187200
c.object_id
# => 72187200
如果我想获取数组 a 的副本,而不是引用的,我应该使用哪种方法?
然后使用a.dup。这里记录了Object#dup
a = [1,2,3,4]
b = a.dup
c = a.dup
a.object_id
# => 82139270
b.object_id
# => 82139210
c.object_id
# => 82134600
a.insert(0,0) # => [0, 1, 2, 3, 4]
b # => [1, 2, 3, 4]
c # => [1, 2, 3, 4]
Array#to_a说:返回 self。如果在 Array 的子类上调用,则将接收者转换为 Array 对象。
所以它不会根据您的需要提供帮助。
【讨论】:
这是因为Array#to_a returns self,所以两个变量都包含对同一个Array对象的引用。为了获得具有相同内容的新数组,您可以使用dup 或clone (read about the differences between dup and clone):
a = [1, 2, 3]
b = a.dup
a << 4
a #=> [1, 2, 3, 4]
b #=> [1, 2, 3]
但请注意,相同的对象引用存储在新数组中。这意味着如果你改变对象本身,它们仍然会在两个数组中发生变化:
a = ['foo', 'bar']
b = a.dup
a[0] << 'baz'
a #=> ["foobaz", "bar"]
b #=> ["foobaz", "bar"]
这是因为dup 和clone 是shallow-copies。
【讨论】:
Array#to_a 返回接收者。这就是为什么b 和c 指的是同一个东西。关于为什么to_a 返回原始数组,原则上,它可以以一种或另一种方式定义,但我猜to_a 的一个用例是将其应用于可能是nil 的变量以确保它变为一个数组。
some_value.to_a # => `[]` if `some_value` is `nil`
在这种用例中,如果接收器已经是一个数组,则无需将数组替换为另一个数组。这在性能上会更可取。
【讨论】:
原因是变量只是对数据的引用。变量存储在内存中;变量保存它们在内存中所在位置的地址。所以当你这样做时:
a = b
这两个变量指向同一个内存位置,因此如果你改变a,b 也会被改变,因为它是同一个对象。
有几种方法可以强制 Ruby 创建对象的另一个副本。最流行的是LBg提到的dup方法。但是请注意,它只是创建一个浅拷贝。如果你运行:
a = ['foo','bar', []]
b = a.dup
a << 'blah'
b #=> ['foo', 'bar', []] as expected but
b[3] << blah
a #=> ['foobar', 'bar', ['blah]]
原因是数组实际上是一个引用数组,嵌套数组在执行dup时没有重复,所以它们是同一个对象。
要创建对象的深层副本,您可以使用 Marshall 模块:
b = Marshal.load(Marshal.dump(a))
但是,通常您实际上并不需要这样做。此外,某些对象不能被复制(例如符号)。
【讨论】:
你可以这样做
b = a.dup
旧帖
如果没有更简单的方法,你可以试试这个
b = a.map {|x| x}
有效
1.9.3-p448 :001 > a = [1,2,3] => [1, 2, 3]
1.9.3-p448 :002 > b = a => [1, 2, 3]
1.9.3-p448 :003 > c = a.map{|x| x} => [1, 2, 3]
1.9.3-p448 :004 > a<<0 => [1, 2, 3, 0]
1.9.3-p448 :005 > b => [1, 2, 3, 0]
1.9.3-p448 :006 > c => [1, 2, 3]
但这是一个浅拷贝。
根据this post,a.dup 是更简单的方法。
【讨论】:
b = a.dup?