Ruby 不包含深度克隆的原因可能与问题的复杂性有关。见文末注释。
要进行“深度复制”、哈希、数组和元素值的克隆,即复制原始元素中的每个元素,以便副本具有相同的值,但新对象,您可以使用这个:
class Object
def deepclone
case
when self.class==Hash
hash = {}
self.each { |k,v| hash[k] = v.deepclone }
hash
when self.class==Array
array = []
self.each { |v| array << v.deepclone }
array
else
if defined?(self.class.new)
self.class.new(self)
else
self
end
end
end
end
如果您想重新定义 Ruby 的 clone 方法的行为,您可以将其命名为 clone 而不是 deepclone(在 3 个位置),但我不知道重新定义 Ruby 的克隆行为将如何影响 Ruby 库, 或 Ruby on Rails,所以警告 Emptor。就个人而言,我不建议这样做。
例如:
a = {'a'=>'x','b'=>'y'} => {"a"=>"x", "b"=>"y"}
b = a.deepclone => {"a"=>"x", "b"=>"y"}
puts "#{a['a'].object_id} / #{b['a'].object_id}" => 15227640 / 15209520
如果您希望您的 类正确地进行深度克隆,它们的new 方法(初始化)必须能够以标准方式深度克隆该类的对象,即,如果给出第一个参数, 假定它是要深度克隆的对象。
假设我们想要一个类 M,例如。第一个参数必须是 M 类的可选对象。这里我们有第二个可选参数z 来预先设置新对象中 z 的值。
class M
attr_accessor :z
def initialize(m=nil, z=nil)
if m
# deepclone all the variables in m to the new object
@z = m.z.deepclone
else
# default all the variables in M
@z = z # default is nil if not specified
end
end
end
z 预设在此处克隆期间将被忽略,但您的方法可能具有不同的行为。这个类的对象会这样创建:
# a new 'plain vanilla' object of M
m=M.new => #<M:0x0000000213fd88 @z=nil>
# a new object of M with m.z pre-set to 'g'
m=M.new(nil,'g') => #<M:0x00000002134ca8 @z="g">
# a deepclone of m in which the strings are the same value, but different objects
n=m.deepclone => #<M:0x00000002131d00 @z="g">
puts "#{m.z.object_id} / #{n.z.object_id}" => 17409660 / 17403500
M 类的对象是数组的一部分:
a = {'a'=>M.new(nil,'g'),'b'=>'y'} => {"a"=>#<M:0x00000001f8bf78 @z="g">, "b"=>"y"}
b = a.deepclone => {"a"=>#<M:0x00000001766f28 @z="g">, "b"=>"y"}
puts "#{a['a'].object_id} / #{b['a'].object_id}" => 12303600 / 12269460
puts "#{a['b'].object_id} / #{b['b'].object_id}" => 16811400 / 17802280
注意事项:
- 如果
deepclone 尝试克隆一个没有以标准方式克隆自身的对象,它可能会失败。
- 如果
deepclone 尝试克隆一个可以以标准方式克隆自身的对象,并且如果它是一个复杂的结构,它可能(并且可能会)对其自身进行浅层克隆。
-
deepclone 不会深度复制哈希中的键。原因是它们通常不被视为数据,但如果您将hash[k] 更改为hash[k.deepclone],它们也会被深度复制。
- 某些元素值没有
new 方法,例如Fixnum。这些对象始终具有相同的对象 ID,并且是复制的,而不是克隆的。
- 请小心,因为当您进行深度复制时,原始中包含相同对象的 Hash 或 Array 的两个部分将在深度克隆中包含不同的对象。