我想在需要时使用默认值重置我的ary。但是,当ary的值发生变化时,我无法弄清楚如何不更改默认值。
> default = {"a"=>[], "b"=>[], "c"=>[]}
=> {"a"=>[], "b"=>[], "c"=>[]}
> ary = default.clone
=> {"a"=>[], "b"=>[], "c"=>[]}
> ary["a"] << "foo"
=> ["foo"]
> default
=> {"a"=>["foo"], "b"=>[], "c"=>[]}
答案 0 :(得分:7)
你在这里发现的是Hash#clone
只做一个浅层克隆,即它只复制自身而不是它内部引用的对象。
有许多“深层克隆”宝石可以解决这个特定问题,或者您可以编写自己的宝石来解决它:
class Hash
def deep_clone
Hash[collect { |k,v| [ k, v.respond_to?(:deep_clone) ? v.deep_clone : v ] }]
end
end
class Array
def deep_clone
collect { |v| v.respond_to?(:deep_clone) ? v.deep_clone : v }
end
end
这将允许您根据需要克隆任意Hash和Array对象。
答案 1 :(得分:3)
clone和dup都会创建对象的浅表副本,从而导致此行为。我不确定实现深层复制的正确方法是什么,而不是:
ary = default.clone
尝试:
ary = Marshal.load(Marshal.dump(default))
这取自ruby 1.8.7上的实时2.3.8环境
答案 2 :(得分:2)
clone
只执行浅拷贝,这就是为什么克隆你的散列仍然保持所有指向同一嵌套数组的原因。
您可以通过转储然后加载对象值来通过Marshal
类来避免这种情况:
> default = {"a" => [], "b" => [], "c" => []}
=> {"a"=>[], "b"=>[], "c"=>[]}
> ary = Marshal.load(Marshal.dump(default))
=> {"a"=>[], "b"=>[], "c"=>[]}
> ary["a"] << "foo"
=> ["foo"]
> default
=> {"a"=>[], "b"=>[], "c"=>[]}
答案 3 :(得分:2)
class Object
def deep_clone
Marshal::load(Marshal.dump(self))
end
end
default = {"a"=>[], "b"=>[], "c"=>[]}
ary = default.deep_clone
ary["a"] << "foo"
default {"a"=>[], "b"=>[], "c"=>[]}
答案 4 :(得分:2)
执行此操作的方法如下:
ary = Marshal.load(Marshal.dump(default))
答案 5 :(得分:2)
根据您的想法,编写深度克隆方法的一种更简单的替代方法可能是编写一个方法,每次调用它时都会创建一个新的默认数组:
def default
{"a"=>[], "b"=>[], "c"=>[]}
end
ary = default #=> {"a"=>[], "b"=>[], "c"=>[]}
ary["a"] << "foo" #=> {"a"=>["foo"], "b"=>[], "c"=>[]}
default #=> {"a"=>[], "b"=>[], "c"=>[]}
当然,如果默认哈希的内容在程序过程中发生变化,这将无法工作,您将不得不研究克隆或编组技术,但如果内容得到修复,这可能会更多直截了当的解决方案。