default value are not properly reflected for the same keys from 2 different but closely related ruby code

StackOverflow https://stackoverflow.com/questions/14304324

  •  15-01-2022
  •  | 
  •  

Question

Please follow the below code :

Part-I

> h = { "a" => 100, "b" => 200 }
=> {"a"=>100, "b"=>200}
> h.default = "Go fish"
=> "Go fish"
> h["a"]
=> 100
> h["z"]
=> "Go fish"
> h.default = proc do |hash, key|
*   hash[key] = key + key
> end
=> #<Proc:0x208bee8@(irb):5>
> h[2]
=> #<Proc:0x208bee8@(irb):5>
> h["cat"]
=> #<Proc:0x208bee8@(irb):5>

Part-II

> h = { "a" => 100, "b" => 200 }
=> {"a"=>100, "b"=>200}
> h.default_proc = proc do |hash, key|
*   hash[key] = key + key
> end
=> #<Proc:0x1e21df8@(irb):2>
> h[2]
=> 4
> h["cat"]
=> "catcat"

Now I am surprised to see, why h[2] and h["cat"] giving different output for the two codes in part-I and part-II.

Could you anyone please explain?

Was it helpful?

Solution

  • Hash#default= sets the value to be returned in case there is no such key. You set that to a proc in Part-I, and that is what you see being returned.
  • Hash#default_proc= sets the proc to be called on itself and the key in case there is no such key. If you do hash[2] = 2 + 2, then hash[2] will return 4. If you do hash["cat"] = "cat" + "cat", then hash["cat"] will return "catcat".

OTHER TIPS

Why? (Philosophical)

What if you wanted a hash of Procs? You can't just set the default value to be a Proc to run, because there wouldn't be a (trivial) mechanism to distinguish it from a Proc to return.

Maps of Procs might be used to implement a simple state machine or external DSL.

Why? (Technical)

Because that's the way [] is written:

VALUE
rb_hash_aref(VALUE hash, VALUE key)
{
    st_data_t val;

    if (!RHASH(hash)->ntbl || !st_lookup(RHASH(hash)->ntbl, key, &val)) {
        if (!FL_TEST(hash, HASH_PROC_DEFAULT) &&
            rb_method_basic_definition_p(CLASS_OF(hash), id_default)) {
            return RHASH_IFNONE(hash);
        }
        else {
            return rb_funcall(hash, id_default, 1, key);
        }
    }
    return (VALUE)val;
}

As the docs to default say:

It is not possible to set the default to a Proc that will be executed on each key lookup.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top