January 26, 2005

Conditional initialization in Ruby

Exploring Ruby further, I came accross the || operator. It behaves like the boolean or operator in other languages (Java, C++) but with a catch: it returns the value of the last evaluated term. Since evaluation is lazy (from left to right), the last evaluated term is either the first non-false (I'll qualify this later on, bear with me) term or the last term in the expression.

More concretely, the expression d = a || b || c will assign the value of a to d if a is non-false, the value of b if a is false and b is non-false, or the value of c otherwise.

A non-false value in Ruby is anything that's neither nil, nor false. Thus, values such as true, 1, 0, "a", [] are all non-false.

The || operator can be combined with the = operator in a self-assignment statement: a ||= expression, which is a shorthand for a = a || expression.

A statement of that kind is essentially a conditional initializer: if a is nil (i.e., not initialized), initialize it with the value of expression. Otherwise, leave it untouched.

The conditional initialization idiom has at least one interesting usage. Before we get to it, it is important to note that in Ruby every statement returns a value, which means that any statement can be used in place of an expression. For example, one could conceivably do:

(if true then 8 end) + 3

Ok, on to the example now...

It is common to have to keep track of lists of items associated with certain key values. The obvious way to implement that is with a map of keys to lists of values. In Java, the code for adding an element to such structure would typically look like this:

void addToList(Object key, Object value)
{
    List list = (List) this.map.get(key);
    if (list == null) {
         list = new ArrayList();
         this.map.put(key, list);
     }

     list.add(value);
}

In Ruby, using the conditional initialization idiom, it could be written more compactly as:

def addToList(key, value)
     (@map[key] ||= []) << value
end


The @map[key] ||= [] piece performs the conditional initialization of the list associated with key and the (...) << value part adds the value to the existing or newly created list.

Ok, ok... you might be thinking, while the Java syntax is more verbose, is certainly more readable. That may be true, but in that case you could still write:

def addToList(key, value)
     list = (@map[key] ||= [])
     list << value
end

Which is still more compact than in Java but in my opinion does not sacrifice readability.

Pretty neat, eh?

Labels:

5 Comments:

Anonymous Anonymous said...

I HATE code like that... So I offer this alternative:

Initialize @map correctly and make your code totally readable!

def initialize
  @map = Hash.new { |h,k| h[k] = [] }
end

def addToList(key, val)
  @map[key] << val
end

-- zenspider

February 02, 2005 9:29 AM  
Blogger Martin said...

I didn't know you could construct a Hash with a default initializer. That's even better!

This shows that when switching languages it is usually not good to just translate the syntax. One has to learn how to *think* in that language.

Thanks for the tip!

February 02, 2005 10:08 AM  
Blogger Sergio Negrinnie said...

excellent article about Java, I have a Get Viagra Online website and this information is very useful to improve my site!!

November 05, 2009 12:13 PM  
Blogger Patrick D said...

hey great information about Conditional initialization in Ruby, where do you get this information??? I would like to have some update about Cialis Online

November 11, 2009 12:57 PM  
Anonymous Anonymous said...

Nice work and thanks!
Running
Adidas currently manufactures several running shoesNike shoes, including the adiStar Control 5, the adiStar Ride
Cheap nike shoes
Discount nike shoes
the Supernova Sequence and the Supernova Cushion 7, among others.
Nike shox r4
nike shox shoes
Adidas also uses kangaroo leather to make their more expensive shoes.
Association football
One of the main focuses of Adidas is football kit and associated equipment.
puma cat
cheap sport shoes
Adidas also provides apparel and equipment for all teams in Major League Soccer. Adidas remain a major company in the supply of team kits for international football teams.
cheap nike shox
cheap nike max
Adidas also makes referee kits that are used in international competition and by many countries and leagues in the world. In the United States, referees wear the Adidas kits in MLS matches even though the primary referee supplier is Official Sports.
nike tn dollar
nike running shoes
The company has been an innovator in the area of footwear for the sport with notable examples including development of the Copa Mondial moulded boot on firm dry pitches for forty years.
nike air max tn
puma shoes
Adidas became renowned for advancing the "Predator" boot design.This design featured a ribbed rubber structure for the upper leather of the shoe, used to accent the movement of the ball.
discount puma shoes
puma mens shoes
The Predator also features the Craig Johnston invented "Traxion" sole. As the development and popularity of Football continued Adidas played a leading role in shaping the style of the play itself.
puma running shoes
puma shoes
FIFA, the sports governing body, commissioned specially designed footballs for use in its own World Cup tournaments to favour more attacking play.
ghd hair straighteners mk4
hair straightners
ghd iv styler hair straightener
ghd hair straightners
cheap ghd hair straighteners

December 08, 2009 7:08 PM  

Post a Comment

<< Home