如何在空数组的哈希上使用默认值?

时间:2011-05-09 20:43:51

标签: ruby-on-rails ruby

我想在需要时使用默认值重置我的ary。但是,当ary的值发生变化时,我无法弄清楚如何不更改默认值。

> default = {"a"=>[], "b"=>[], "c"=>[]}
=> {"a"=>[], "b"=>[], "c"=>[]} 

> ary = default.clone
=> {"a"=>[], "b"=>[], "c"=>[]} 

> ary["a"] << "foo"
=> ["foo"] 

> default
=> {"a"=>["foo"], "b"=>[], "c"=>[]} 

6 个答案:

答案 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"=>[]}

当然,如果默认哈希的内容在程序过程中发生变化,这将无法工作,您将不得不研究克隆或编组技术,但如果内容得到修复,这可能会更多直截了当的解决方案。