提供最简单的示例,其中ruby中需要深层复制

时间:2016-08-30 13:07:17

标签: ruby deep-copy shallow-copy

我真的不明白浅版和深版之间的区别。当我测试它时,Ruby的#dup似乎会创建一个深层副本。

文档说:

Produces a shallow copy of obj---the instance variables of obj are
copied, but not the objects they reference.

但是当我测试它时,它似乎改变了它们引用的对象。

class Klass
  attr_accessor :name
end

a = Klass.new
a.name = "John"
b = a.dup
b.name = "Sue"
puts a.name # John

@nameobjects they reference之一时,为什么浅层副本就足够了? 什么是最简单的示例,其中需要深层复制?

2 个答案:

答案 0 :(得分:1)

试试这个:

class Klass
  attr_accessor :name
end

a = Klass.new
a.name = Klass.new #object inside object
a.name.name = 'George'
b = a.dup
puts b.name.name # George

b.name.name = 'Alex'
puts a.name.name # Alex

另请注意(see info)

  

使用dup时,对象已扩展的所有模块   不会被复制。

编辑:关于字符串的注释(这很有趣) 引用的字符串不会在原始方案中复制。通过这种情况证明了这一点:

a.name = 'George'
puts a.name.object_id # 69918291262760    

b = a.dup
puts b.name # George
puts b.name.object_id # 69918291262760  

b.name.concat ' likes tomatoes' # append to existing string
puts b.name.object_id # 69918291262760  

puts a.name # George likes tomatoes

这可以按预期工作。不会复制引用的对象(包括字符串),并将共享引用。

那么为什么原始例子不会出现呢?这是因为当你将b.name设置为不同的东西时,你将它设置为一个新的字符串。

   a.name = 'hello' 

对此非常简短:

   a.name = String.new('hello')

因此在原始示例中,a.name& b.name不再引用同一个对象,可以查看object_id来查看。

请注意,Fixnum,float,true,false或symbol不是这种情况。这些对象以浅拷贝复制。

答案 1 :(得分:1)

您显示的示例没有描述深拷贝和浅拷贝之间的区别。相反,请考虑以下示例:

class Klass
  attr_accessor :name
end

anna = Klass.new
anna.name = 'Anna'

anna_lisa = anna.dup
anna_lisa.name << ' Lisa'
# => "Anna Lisa"

anna.name
# => "Anna Lisa"

通常,dupclone都只是复制您调用方法的实际对象。没有其他引用的对象,如上例中的name字符串是重复的。因此,在复制之后,原始对象和复制对象都指向同一个名称字符串。

对于deep_dup,通常所有(相关的)引用对象也是重复的,通常是无限深度。由于这对于所有可能的对象引用来说都很难实现,因此通常人们依赖于特定对象(如哈希和数组)的实现。

相当通用的深层复制的常见解决方法是使用Ruby的Marshal类来序列化对象图并直接再次反序列化它。

anna_lena = Marshal.load( Marshal.dump(anna))

这会创建新对象,实际上是一个deep_dup。由于大多数对象立即支持编组,因此这是一种相当强大的机制。请注意,您应该永远不要解组(即load)用户提供的数据,因为这会导致远程执行代码漏洞。