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 Joyce wrote:

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?

  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

···

--
Vincent Fourmond, PhD student (not for long anymore)
http://vincent.fourmond.neuf.fr/

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 <vince.hetk@gmail.com> wrote:

John Joyce wrote:
> 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?

  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.

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

--
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black\)
   (See what readers are saying! http://www.rubypal.com/r4rrevs.pdf\)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)

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 Mar 23, 7:53 am, Vince H&K <vince.h...@gmail.com> wrote:

John Joyce wrote:
> 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?

  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.

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

···

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

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

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

  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

···

from :0

--
Vincent Fourmond, PhD student (not for long anymore)
http://vincent.fourmond.neuf.fr/

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

John Joyce <dangerwillrobinsondanger@gmail.com> 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.)

···

---------------------------------
TV dinner still cooling?
Check out "Tonight's Picks" on Yahoo! TV.

i have used that yet another way:

this or that if foo

but it depens on the return-value...

···

On 3/24/07, Trans <transfire@gmail.com> wrote:

On Mar 23, 7:53 am, Vince H&K <vince.h...@gmail.com> wrote:
> John Joyce wrote:
> > 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?
>
> 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.

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.

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 Joyce <dangerwillrobinsondanger@gmail.com> wrote:

On Mar 23, 2007, at 9:59 PM, David A. Black wrote:
>
> 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

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/

Hi --

···

On 3/23/07, John Joyce <dangerwillrobinsondanger@gmail.com> wrote:

On Mar 23, 2007, at 9:59 PM, David A. Black wrote:
>
> 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

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

--
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black\)
   (See what readers are saying! http://www.rubypal.com/r4rrevs.pdf\)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)

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.

···

On 3/24/07, Vince H&K <vince.hetk@gmail.com> wrote:

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).

--
Rick DeNatale

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

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

···

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

Hi --

On 3/23/07, John Joyce <dangerwillrobinsondanger@gmail.com> wrote:

On Mar 23, 2007, at 9:59 PM, David A. Black wrote:
>
> 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

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:

Hi --

···

On 3/26/07, Rick DeNatale <rick.denatale@gmail.com> wrote:

On 3/24/07, Vince H&K <vince.hetk@gmail.com> wrote:
> 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).

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.

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

--
Q. What is THE Ruby book for Rails developers?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black\)
   (See what readers are saying! http://www.rubypal.com/r4rrevs.pdf\)
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)

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?

···

On 3/27/07, David A. Black <dblack@wobblini.net> wrote:

Hi --

On 3/26/07, Rick DeNatale <rick.denatale@gmail.com> wrote:
> On 3/24/07, Vince H&K <vince.hetk@gmail.com> wrote:
> > 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).
>
> 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.

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

--
Rick DeNatale

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

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?

···

On Mar 27, 10:39 am, "Rick DeNatale" <rick.denat...@gmail.com> wrote:

On 3/27/07, David A. Black <dbl...@wobblini.net> wrote:

> 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?