Sunday, July 22, 2007

Ruby: Multiple ifs (unless)

I recently ran into some code similar to the snippet below.

item.currency if item.currency != :usd unless item.nil?

Based on reading the code I assumed it worked as expected; however, having never actually tried it myself I decided to hit irb for a moment with the following code.

p 3 unless p 2 unless p 1

Sure, I'm not using if, but if and unless have the same precedence, so I thought the example was good enough.

p 3 unless p 2 unless p 1
# >> 1
# >> 2
# >> 3

The output shows the execution order: The rightmost if (or unless) is evaluated first and then it moves to the next conditional immediately to it's left.

Of course, the statement could be rewritten simply using ||.

item.currency unless item.nil? || item.currency == :usd

Due to short circuit evaluation neither statement executes item.currency != :usd if item.nil?.

2 comments:

  1. This works because "if" or "unless" are themselves normal expressions (as opposed to regular expressions ;).

    As mentioned in the docs however, "This path leads to the gates of madness."!

    ReplyDelete
  2. Jay,

    I'm not sure that your example expression:

    p 3 unless p 2 unless p 1

    really shows what you want to.

    The problem is that the p method returns nil, so that none of the unless logical expressions evaluate to true.

    Here's a more realistic experiment:
    irb(main):001:0> p 2
    2
    => nil
    irb(main):002:0> def q(a)
    irb(main):003:1> p a
    irb(main):004:1> a
    irb(main):005:1> end
    => nil
    irb(main):006:0> q 3 unless q 2 unless q 1
    1
    => nil

    the q method returns it's argument with the side effect of printing it. Note that the unless statement modifiers are evaluated right to left, so only the last invokation of q happens.

    By the way, one of your recent blogs just triggered me to write this.

    ReplyDelete

Note: Only a member of this blog may post a comment.