14 February 2007

Ruby Hashes are cool

Although I don't program in Ruby regularly, I subscribe to the weekly Ruby Quiz. It's a way of getting a hang of the syntax and learning what kind of problems the Ruby language is suited for.

This week's quiz was an interesting mixture of questions. It had a number of simple problems all designed to be solved in one line of code. The quiz recommended the pursuit of finesse over brevity, so I was keen to see what the Ruby gurus came up with.

Question #9 looked interesting:

Given a wondrous number Integer, produce the sequence (in an Array). A wondrous number is a number that eventually reaches one, if you apply the following rules to build a sequence from it. If the current number in the sequence is even, the next number is that number divided by two. When the current number is odd, multiply that number by three and add one to get the next number in the sequence.

Therefore, if we start with the wondrous number 15, the sequence is [15, 46, 23, 70, 35, 106, 53, 160, 80, 40, 20, 10, 5, 16, 8, 4, 2, 1].

James Edward Gray II came up with this amazing solution:

Hash.new { |h, n| n == 1 ? [1] : [n] + h[n % 2 == 0 ? n/2 : n*3+1] } [quiz]

The solution creates a fallback element accessor for the hash. When a value is looked up in the hash, it derives the value based on the calculation specified for following the sequence of a wondrous number. It doesn't actually store anything in the hash (for brevity rather than efficiency, I guess), it just uses it as a convenient way of declaring the block.

The technique of creating a fallback element accessor for the Hash is described by Mentalguy:

When you create a Hash object in Ruby, you have the option of providing a block; when a hash lookup fails, this block will be called and its result returned instead of nil. The block is passed two parameters: the hash itself, and the requested key.

Such a neat idea, and so easy to write. I like it a lot.