This post originated from an RSS feed registered with Ruby Buzz
by Andrew Johnson.
Original Post: Hash with block or Block with hash?
Feed Title: Simple things ...
Feed URL: http://www.siaris.net/index.cgi/index.rss
Feed Description: On programming, problem solving, and communication.
Not exactly new, passing blocks to Hash.new has been around for a
while (since the 1.8.0 release I think). This can be used to supply a
default value or action for accessing keys not in the hash.
hash = Hash.new {|h,k| h[k] = "Default Value"}
hash = Hash.new {|h,k| raise "No Such Key #{k}"}
Note, the objects yielded to the block are the hash and the key (not a key
and a value, which is an easy but nonsensical mistake to make).
In the first example above we didn’t just return a default value, we
also assigned to the appropriate key in the hash — next time we try
to access the hash using this key we will get the default value directly
instead of having to evaluate the block again. This point of view — a
hash with a block attached to generate default values — is useful,
but not terribly interesting.
Turning the view around we have a block with an attached cache —
giving us simple memoization. Here’s a little memoized factorial
calculator:
fact = Hash.new {|h, n| n < 2 ? h[n] = 1 : h[n] = h[n-1] * n}
p fact[4] #--> 24
p fact #--> {1=>1, 2=>2, 3=>6, 4=>24}
Now, there are obvious limitations: the [] hash method takes a
single object as a key so you can’t pass multiple parameters directly
(you’d need to wrap them in an array or something), and, parameter
caching will be based on the #hash method of the objects used as
keys. Another limitation is cache size and expiry — in which case you
might want to try the memoize module
available on the RAA.