私が最近これを行う通常の方法は次のとおりです。
h = Hash.new { |h,k| h[k] = {} }
これにより、欠落しているキーのエントリとして新しいハッシュを作成するハッシュが得られますが、2番目のレベルのキーには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'] -> {}
上記のコードは、デフォルトのprocが同じデフォルトのprocで新しいハッシュを作成するハッシュを作成します。したがって、見えないキーのルックアップが発生したときにデフォルト値として作成されたハッシュは、同じデフォルトの動作になります。
編集:詳細
Rubyハッシュを使用すると、新しいキーのルックアップが発生したときにデフォルト値を作成する方法を制御できます。指定すると、この動作はオブジェクトとしてカプセル化され、メソッドとメソッドProc
を介して到達可能になります。デフォルトのprocは、ブロックをに渡すことによって指定することもできます。default_proc
default_proc=
Hash.new
このコードを少し分解してみましょう。これは慣用的なルビーではありませんが、複数の行に分割する方が簡単です。
1. recursive_hash = Hash.new do |h, k|
2. h[k] = Hash.new(&h.default_proc)
3. end
recursive_hash
1行目は、変数をnewとして宣言しHash
、ブロックをrecursive_hash
'sとして開始しますdefault_proc
。ブロックには2つのオブジェクトが渡されます。キールックアップが実行されてh
いるHash
インスタンスである、とルックアップされてk
いるキーです。
2行目では、ハッシュのデフォルト値を新しいHash
インスタンスに設定しています。このハッシュのデフォルトの動作は、ルックアップが発生しているハッシュ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]
が作成されたとき、それは'sdefault_proc
によって提供されました。これには2つの効果があります。recursive_hash
default_proc
- のデフォルトの動作
recursive_hash[:foo]
は。と同じrecursive_hash
です。
recursive_hash[:foo]
'sによって作成されたハッシュのデフォルトの動作は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=>{}}}}