Having worked with Python for a while, I am trying to pick up Ruby, especially for some of my work with logstash. While trying out a small program in Ruby, I got stumped with a peculiar trait of Ruby hashes with default values. It made me lose an hour of my life I am not going to get back. :(
In Python, dictionaries with default values is not part of the core language, and need to be imported from a standard library. For some reason I still don’t know, you cannot set a simple default value, you need supply a function object which is going to create a value for you. :/
So if I want to create a default dictionary in Python, you need to do something like this:
>>> from collections import defaultdict >>> d = defaultdict(lambda: 5)Now when you access any non-existent key in this hash, the hash gets magically populated with that key and the default value. See below.
>>> if d["one"]: print "not empty" ... not empty >>> >>> print dict(d) {'one': 5} >>>Ruby hashes support default values in the core language. So you can do something like:
>> d = Hash.new(5) => {} >> puts d["a"] 5 => nil >> d => {}Wait! See the difference? Evaluating a non-existing hash position is Ruby returns the default value, but unlike Python, it doesn’t set the value!
After it drilled down to this quirk, I looked around and found some interesting articles.
This article from 2008 by David Black was particularly interesting. He points out in this article how this Hash behaviour breaks a very popular Ruby idiom.
Normally, the idiomatic Ruby way to initialize or return a variable goes like this:
>> d = Hash.new => {} >> d["name"] ||= "Skye" => "Skye" >> d => {"name"=>"Skye"}However, if you use a Hash with a default value, it breaks this idiom because
evaluating a non-existing key returns the default value. However, you would
intuitively expect the ||=
operator to at least set the missing key to the
default value! But that doesn’t happen due to the peculiar treatment to that
operator by Ruby. Ruby evaluates A ||= B
NOT to A = A || B
, but A || A=B
.
This never lets the value to be assigned to keys in default hashes.
Some gotcha lurking there in an otherwise beautiful language.
Another nice ref: http://www.rubyinside.com/what-rubys-double-pipe-or-equals-really-does-5488.html
comments powered by Disqus