Question about if in Ruby

I’ve seen this in Rails, but it is just ERb, so it is Ruby…

<%= link_to ‘Previous page’, { :page =>
@product_pages.current.previous } if @product_pages.current.previous %>

I’m curious how it is different from this…

<%= if @product_pages.current.previous link_to (‘Previous page’,
{ :page => @product_pages.current.previous })
end
%>

I’ve seen this kind of if statement reversal a few times now in Ruby
code. I call it do-if because it looks similar to do-while, but I am
curious if reversing the condition affects the interpreter’s speed at
all. If they are the same, then isn’t the standard if conditional
better for human legibility and understanding?

John J. wrote:

<%= if @product_pages.current.previous link_to (‘Previous page’, {
:page => @product_pages.current.previous })
end
%>

I’ve seen this kind of if statement reversal a few times now in Ruby
code. I call it do-if because it looks similar to do-while, but I am
curious if reversing the condition affects the interpreter’s speed at
all. If they are the same, then isn’t the standard if conditional better
for human legibility and understanding?

The first form of the statement is valid for only one instruction.
Both

if something
do_something
end

and

do_something if something

are equivalent, but when it comes to

if a
one_thing
another_thing
end

you simply can’t use the other form, as it applies to only one
instruction.

The whole thing about this is readability. Depending on the cases and
your personal tastes, it will look more logical to use one form or
another (for one instruction). What is great in Ruby (IMHO), is that you
have many ways to express the same things, some of them quite verbose,
which means you can write code sometimes nearly as readable as pure
English text.

Cheers,

Vince

Thanks Vince!
That’s precisely what I wanted to know.
I had seen a few cases where the conditional coming later did make
sense as an English sentence. I’ve seen unless used in a similar way.
It does read well (after knowing some of the Ruby of course.)

Hi –

On 3/23/07, Vince H&K [email protected] wrote:

for human legibility and understanding?

are equivalent, but when it comes to

if a
one_thing
another_thing
end

you simply can’t use the other form, as it applies to only one instruction.

Just for completeness (I’m not recommending it), I’ll mention that you
can do:

one_thing if begin
another_thing
a_third_thing
end

David

On Mar 23, 2007, at 9:59 PM, David A. Black wrote:

David
Completeness is good. But that is pretty hideous stuff.
Starting to look like some cryptic C or ambitious BASIC:
logic spaghetti.

You can also do this:

begin
one_thing
second_thing
do_more
end if true == true

-OR-

begin
one_thing
two_thing
three_thing
end if begin
another_thing
another_better_thing
end

But, of course, we’re just getting ridiculous now. :wink:

–Jeremy

On 3/23/07, John J. [email protected] wrote:

David

Completeness is good. But that is pretty hideous stuff.
Starting to look like some cryptic C or ambitious BASIC:
logic spaghetti.


http://www.jeremymcanally.com/

My free Ruby e-book:
http://www.humblelittlerubybook.com/book/

My blogs:

http://www.rubyinpractice.com/

On 23.03.2007 16:53, David A. Black wrote:

                 another_thing

I wouldn’t worry too much:

$ find . -name “*.rb” -exec grep “if begin” ‘{}’ ‘;’ | wc -l
0

:slight_smile:

Same here:

17:02:44 [~]: ruby -e ‘puts Dir["/usr/lib/ruby/**/*.rb"].inject(0)
{|c,f| /if\s+begin/ =~ File.read(f) ? c+1 : c}’
0
17:03:10 [~]:

:slight_smile:

robert

Hi –

On 3/23/07, John J. [email protected] wrote:

David

Completeness is good. But that is pretty hideous stuff.
Starting to look like some cryptic C or ambitious BASIC:
logic spaghetti.

I wouldn’t worry too much:

$ find . -name “*.rb” -exec grep “if begin” ‘{}’ ‘;’ | wc -l
0

:slight_smile:

David

Trans wrote:

There is one other difference.

if a = 1
p a
end

p a if a = 1

The later does not assign the a.

This is not exactly true (at least on ruby 1.8.6):

irb(main):001:0> if a = 1
irb(main):002:1> p a
irb(main):003:1> end
1
=> nil
irb(main):004:0> p b if b = 2
NameError: undefined local variable or method `b’ for main:Object
from (irb):4
from :0

It doesn’t create the variable b in the same scope as the one of the
instruction. However:

irb(main):007:0> a = 1
=> 1
irb(main):008:0> p a if a = 2
2
=> nil
irb(main):009:0> a
=> 2

It does assign it if it already exists in the current scope. Works as
well in ruby (it is not an irb artifact).

Cheers,

Vince

wat thanxx…i did tpp on "ruby on rails…which is nao even
selectedddd…

John J. [email protected] wrote: Thanks Vince!
That’s precisely what I wanted to know.
I had seen a few cases where the conditional coming later did make
sense as an English sentence. I’ve seen unless used in a similar way.
It does read well (after knowing some of the Ruby of course.)

On Mar 23, 7:53 am, Vince H&K [email protected] wrote:

end

if something
one_thing
another_thing
end

you simply can’t use the other form, as it applies to only one instruction.

There is one other difference.

if a = 1
p a
end

p a if a = 1

The later does not assign the a.

btw you can also use parens for more than one call.

( do_this ; do_that ) if something

or

( do_this
do_that
) if something

T.

On 3/24/07, Vince H&K [email protected] wrote:

    from :0

=> 2

It does assign it if it already exists in the current scope. Works as
well in ruby (it is not an irb artifact).

In fact it DOES assign:
rick@frodo:/public/rubysource/ruby1.8.5$ irb
irb(main):001:0> a
NameError: undefined local variable or method a' for main:Object from (irb):1 irb(main):002:0> p a if a = 1 (irb):2: warning: found = in conditional, should be == NameError: undefined local variable or method a’ for main:Object
from (irb):2
irb(main):003:0> a
=> 1

I think that it’s more accurate to say that the difference between

if a = 1

end

and

… if a = 1

is that the latter checks for the existence of a and raises a
NameError after the assignment if the variable didn’t exist before.

Is this a bug? Maybe. It is kind of an edge case, notice the warning
indicating that you really should be using a comparison instead of an
assignment.


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

i have used that yet another way:

this or that if foo

but it depens on the return-value…

On 3/27/07, David A. Black [email protected] wrote:

=> nil
irb(main):008:0> p a if a = 2
irb(main):001:0> a
I think that it’s more accurate to say that the difference between
NameError after the assignment if the variable didn’t exist before.

It’s actually the ‘a’ in ‘p a’ that raises the error, not the one in
the assignment:

true if a = 1 # no error
b = 1; p b if a = 1 # no error

True of course.

What’s interesting is that the if a=1 doesn’t create the variable,
since it does have to be executed first.

I wouldn’t recommend actually writing code like this, but it does seem
a bit inconsitent.

A quirk of the ruby parser? eval?

Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

Hi –

On 3/26/07, Rick DeNatale [email protected] wrote:

The later does not assign the a.
from (irb):4
irb(main):009:0> a
irb(main):002:0> p a if a = 1

end

and

… if a = 1

is that the latter checks for the existence of a and raises a
NameError after the assignment if the variable didn’t exist before.

It’s actually the ‘a’ in ‘p a’ that raises the error, not the one in
the assignment:

true if a = 1 # no error
b = 1; p b if a = 1 # no error

David

On Mar 27, 10:39 am, “Rick DeNatale” [email protected] wrote:

On 3/27/07, David A. Black [email protected] wrote:

A quirk of the ruby parser? eval?

I’m too lazy to look it up, but this has been discussed on the ML
several times before. (I personally posted about it early in my Ruby
career and discussed it then.)

It’s a quirk of the parser, which sees variable assignments in top-
down byte order of the source file, not post-parsed execution order.
I’ve frequently wished that it would work for cases where you want to
cache and test the return value of a method before performing a simple
action on it.

To be clear:

Works just fine

if foo=bar() && !foo.nil?
p foo.jim
end

Doesn’t parse/compile/whatever, though the

end call sequence should be the same.

p foo.jim if foo=bar() && !foo.nil?