0

I was playing to non-existence key of hash h1.but got surprised when i was seeing some errors and with their resolution.I wanted to know how the recursive call doing the job internally to handle the errors.

Part-I

here when tried h1[2][3] caused error. okay in next part I have resolved it.

irb(main):002:0> h1=Hash.new()
=> {}
irb(main):003:0> h1[2]
=> nil
irb(main):004:0> h1[2][3]
NoMethodError: undefined method `[]' for nil:NilClass
        from (irb):4
        from C:/Ruby193/bin/irb:12:in `<main>'

Part-II

Now how the Hash definition below handles the previous error. What internal algorithm ran,which was bot possible by Part-I. I know the below sysntx resolved it but i want to see internal screen how it has done job.

irb(main):005:0> h1 = Hash.new do |h,k|
irb(main):006:1*   h[k] = Hash.new()
irb(main):007:1> end
=> {}
irb(main):008:0> h1[2]
=> {}
irb(main):009:0> h1[2][3]
=> nil

Can the recursive call be fixed? say h1[1][2][3] and h1[1][2][3][4] so on.

When I was calling Key by h1[2]- here i know that 2 is a key i was looking for.Now h1[1][2] - in that case the call looking for key with [1][2] - am I correct? If i am not correct then how the real thing is working from back-end - wanted to know that.

Can anyone help me here to understand?

Thanks,

Arup Rakshit
  • 116,827
  • 30
  • 260
  • 317

2 Answers2

3
h = Hash.new{|h, k| h[k] = Hash.new(&h.default_proc)}
#demo:
p h[:a][:b][:c]=1 # =>{:a=>{:b=>{:c=>1}}}
steenslag
  • 79,051
  • 16
  • 138
  • 171
  • Could you please explain,how your code run with your example, breaking each of its part! – Arup Rakshit Jan 13 '13 at 12:09
  • It's recursive. It is not my idea - I picked it up somewhere, probably Rubytalk. The block after Hash.new defines what should happen when a key is not found in the hash -AKA the default proc. Here we define that a new hash should be created as the new value for the new key. This new hash also has a block defining what should happen with an unknown key, which happens to be the same default proc we are defining. – steenslag Jan 13 '13 at 13:34
2

If I understand your question correctly I think you can do what you want with a recursive Proc:

p = Proc.new { Hash.new { |hash, key| hash[key] = p.call } }
h = Hash.new { |hash, key| hash[key] = p.call }

then:

h[0][0][0] -> {}

Explanation:

p is a Ruby Proc. Procs are similar to Ruby blocks but are fully-fledged objects and can therefore be stored in variables.

Procs can be recursive - they can call themselves and we make use of that in this example to create a new default Hash value no matter how many levels down the tree of hashes we call. The Proc#call method invokes the Proc. Note that the body of the Proc has access to the variables defined outside its scope (it is a closure).

steenslags solution is doing more or less the same thing, but is a more elegant one-liner making use of the Hash#default_proc property to avoid the need to define a Proc variable.

Steve
  • 15,606
  • 3
  • 44
  • 39