这些天我通常这样做的方式是:
h = Hash.new { |h,k| h[k] = {} }
这将为您提供一个散列,该散列创建一个新散列作为缺少键的条目,但为第二级键返回 nil:
h['foo'] -> {}
h['foo']['bar'] -> nil
您可以嵌套它以添加可以通过这种方式处理的多个层:
h = Hash.new { |h, k| h[k] = Hash.new { |hh, kk| hh[kk] = {} } }
h['bar'] -> {}
h['tar']['zar'] -> {}
h['scar']['far']['mar'] -> nil
您也可以使用default_proc 方法无限链接:
h = Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) }
h['bar'] -> {}
h['tar']['star']['par'] -> {}
上面的代码创建了一个散列,其默认过程创建了一个具有相同默认过程的新散列。因此,在查找未见过的键时创建为默认值的哈希将具有相同的默认行为。
编辑:更多细节
Ruby 哈希允许您控制在查找新键时如何创建默认值。指定后,此行为被封装为 Proc 对象,并可通过 default_proc 和 default_proc= 方法访问。也可以通过将块传递给Hash.new 来指定默认过程。
让我们稍微分解一下这段代码。这不是惯用的 ruby,但它更容易分成多行:
1. recursive_hash = Hash.new do |h, k|
2. h[k] = Hash.new(&h.default_proc)
3. end
第 1 行将变量recursive_hash 声明为新的Hash,并开始一个块为recursive_hash 的default_proc。该块传递了两个对象:h,这是正在执行键查找的Hash 实例,以及k,正在查找的键。
第 2 行将散列中的默认值设置为新的 Hash 实例。这个散列的默认行为是通过传递一个Proc 来提供的,该Proc 是从发生查找的散列的default_proc 创建的;即,块本身定义的默认过程。
这是一个 IRB 会话的示例:
irb(main):011:0> recursive_hash = Hash.new do |h,k|
irb(main):012:1* h[k] = Hash.new(&h.default_proc)
irb(main):013:1> end
=> {}
irb(main):014:0> recursive_hash[:foo]
=> {}
irb(main):015:0> recursive_hash
=> {:foo=>{}}
当recursive_hash[:foo] 的哈希被创建时,它的default_proc 由recursive_hash 的default_proc 提供。这有两个效果:
-
recursive_hash[:foo] 的默认行为与recursive_hash 相同。
-
recursive_hash[:foo] 的default_proc 创建的哈希的默认行为将与recursive_hash 相同。
所以,继续 IRB,我们得到以下结果:
irb(main):016:0> recursive_hash[:foo][:bar]
=> {}
irb(main):017:0> recursive_hash
=> {:foo=>{:bar=>{}}}
irb(main):018:0> recursive_hash[:foo][:bar][:zap]
=> {}
irb(main):019:0> recursive_hash
=> {:foo=>{:bar=>{:zap=>{}}}}