[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

why must I initialize this variable?

matt

1/22/2008 5:34:00 PM

Here's a simple variable initialization / scope question. In the
following code:

h = {:test => "cool"}
k = :test
result = nil
puts "#{result}" if (result = h[k])

....if I omit the third line (initializing "result"), I get an error.
Why? Intuitively I would have expected the second part of the 4th line
to create / initialize "result".

Another way of understanding my confusion is to notice that this *does*
work:

h = {:test => "cool"}
k = :test
if (result = h[k]) then puts "#{result}" end

So evidently I'm asking about the difference between the last line of
the first example and the last line of the second example. Intuitively I
would have expected these lines to be absolutely equivalent, but clearly
they are not. I'd like to understand the difference rigorously. Thx! m.

--
matt neuburg, phd = matt@tidbits.com, http://www.tidbits...
Leopard - http://www.takecontrolbooks.com/leopard-custom...
AppleScript - http://www.amazon.com/gp/product/...
Read TidBITS! It's free and smart. http://www.t...
13 Answers

Thomas Wieczorek

1/22/2008 9:05:00 PM

0

Hello!

On Jan 22, 2008 6:40 PM, matt neuburg <matt@tidbits.com> wrote:
> Here's a simple variable initialization / scope question. In the
> following code:
>

First of all I think that you confuse assignment(=) with
comparison(==). Every operation in Ruby returns a value.
In assignment the right hand side is returned
> irb
h = {:test => "cool"}
#returns {:test => "cool"}

In comparison, either true or false are returned. An assignment
returns true if you don't assign _nil_ or _false_.
> irb
if foo = "bar"
puts foo
end
(irb):16: warning: found = in conditional, should be ==
As you see, irb or running Ruby with the '-w' will give you a warning,
that you used an assignment in a

> h = {:test => "cool"}
> k = :test
> result = nil
> puts "#{result}" if (result = h[k])
>

I am not sure if I am right: Here it tried to get the value of result
first, if it doesn't exist, an exception occurs. After looking up
result, it tests the if-clause.

> ...if I omit the third line (initializing "result"), I get an error.
> Why? Intuitively I would have expected the second part of the 4th line
> to create / initialize "result".
>
> Another way of understanding my confusion is to notice that this *does*
> work:
>
> h = {:test => "cool"}
> k = :test
> if (result = h[k]) then puts "#{result}" end
>

Here, you assign, the assignment returns "cool", which is true and
then you print out the existing variable.

I hope you understand what I mean.

Regards,
Thomas

Justin Collins

1/22/2008 9:25:00 PM

0

matt neuburg wrote:
> Here's a simple variable initialization / scope question. In the
> following code:
>
> h = {:test => "cool"}
> k = :test
> result = nil
> puts "#{result}" if (result = h[k])
>
> ...if I omit the third line (initializing "result"), I get an error.
> Why? Intuitively I would have expected the second part of the 4th line
> to create / initialize "result".
>
> Another way of understanding my confusion is to notice that this *does*
> work:
>
> h = {:test => "cool"}
> k = :test
> if (result = h[k]) then puts "#{result}" end
>
> So evidently I'm asking about the difference between the last line of
> the first example and the last line of the second example. Intuitively I
> would have expected these lines to be absolutely equivalent, but clearly
> they are not. I'd like to understand the difference rigorously. Thx! m


I _believe_ this is because the left-hand side is parsed first, as Ruby
works left to right.

You will notice that the variable _does_ get initialized:

irb(main):001:0> puts "#{hi}" if false
=> nil
irb(main):002:0> puts "#{hi}" if hi = "hello"
(irb):2: warning: found = in conditional, should be ==
NameError: undefined local variable or method `hi' for main:Object
from (irb):2
from :0
irb(main):003:0> hi
=> "hello"

But at the time (before evaluating the conditional clause) the variable
does not exist. The code which is run when the conditional succeeds is
what was parsed prior to evaluating the conditional.

At least, that is my understanding. Of course, best not to use
assignment as a conditional as it leads to confusion.

-Justin

matt

1/22/2008 10:23:00 PM

0

Justin Collins <justincollins@ucla.edu> wrote:

> irb(main):001:0> puts "#{hi}" if false
> => nil
> irb(main):002:0> puts "#{hi}" if hi = "hello"
> NameError: undefined local variable or method `hi' for main:Object
> from (irb):2
> from :0

Even weirder! Why does it work in the first case but not in the second.
If you're going to say that 'hi' is undefined, surely it was undefined
in line 001. Yet line 001 raised no error.

So what's wrong with 002? It cannot be the 'puts "#{hi}"' part - you
just proved, with 001, that that part's okay. So apparently it is the
assignment part. Evidently a tailgating "if" refuses to auto-instantiate
a variable. That is what I'm attempting to get clear on. Is that a bug?
Is it expected behavior? m.

--
matt neuburg, phd = matt@tidbits.com, http://www.tidbits...
Leopard - http://www.takecontrolbooks.com/leopard-custom...
AppleScript - http://www.amazon.com/gp/product/...
Read TidBITS! It's free and smart. http://www.t...

matt

1/22/2008 10:23:00 PM

0

Thomas Wieczorek <wieczo.yo@googlemail.com> wrote:

> Hello!
>
> On Jan 22, 2008 6:40 PM, matt neuburg <matt@tidbits.com> wrote:
> > Here's a simple variable initialization / scope question. In the
> > following code:
> >
>
> First of all I think that you confuse assignment(=) with
> comparison(==).

No, I don't. The assignment is the point of the example. Possibly I
should have used double-parens around the assignment to make that clear
(a common C convention).

m.

--
matt neuburg, phd = matt@tidbits.com, http://www.tidbits...
Leopard - http://www.takecontrolbooks.com/leopard-custom...
AppleScript - http://www.amazon.com/gp/product/...
Read TidBITS! It's free and smart. http://www.t...

Justin Collins

1/23/2008 12:43:00 AM

0

matt neuburg wrote:
> Justin Collins <justincollins@ucla.edu> wrote:
>
>
>> irb(main):001:0> puts "#{hi}" if false
>> => nil
>> irb(main):002:0> puts "#{hi}" if hi = "hello"
>> NameError: undefined local variable or method `hi' for main:Object
>> from (irb):2
>> from :0
>>
>
> Even weirder! Why does it work in the first case but not in the second.
> If you're going to say that 'hi' is undefined, surely it was undefined
> in line 001. Yet line 001 raised no error.
>
> So what's wrong with 002? It cannot be the 'puts "#{hi}"' part - you
> just proved, with 001, that that part's okay. So apparently it is the
> assignment part. Evidently a tailgating "if" refuses to auto-instantiate
> a variable. That is what I'm attempting to get clear on. Is that a bug?
> Is it expected behavior? m.
>
>

Again, I am not 100% on this (interpreter implementors would know
better) but it's because the first line does not actually evaluate the
left hand side.

In the second line, the left hand side is parsed and set aside,
something like how a block is, (someone please correct me on this if I
am wrong) pending the evaluation of the conditional:

irb(main):001:0> a = lambda { puts "#{hi}" }
=> #<Proc:0xb7db81a4@(irb):1>
irb(main):002:0> a.call if false
=> nil
irb(main):003:0> a.call if true
NameError: undefined local variable or method `hi' for main:Object
from (irb):1
from (irb):3:in `call'
from (irb):3
from :0
irb(main):004:0> a.call if hi = "hello"
(irb):4: warning: found = in conditional, should be ==
NameError: undefined local variable or method `hi' for main:Object
from (irb):1
from (irb):4:in `call'
from (irb):4
from :0
irb(main):005:0> hi
=> "hello"

So, I guess it is something of a scope issue.

-Justin

Curt Sampson

1/23/2008 11:03:00 PM

0

On 2008-01-23 02:40 +0900 (Wed), matt neuburg wrote:

> puts "#{result}" if (result = h[k])
>
> ...I get an error...
> Another way of understanding my confusion is to notice that this *does*
> work:
>
> if (result = h[k]) then puts "#{result}" end

This is just one of those little annoyances in Ruby. You'll
find things like this all over the place.

You may or may not get used to it. I never have, but I live with it
while waiting for the perfect language to come along.

cjs
--
Curt Sampson <cjs@starling-software.com> +81 90 7737 2974
Mobile sites and software consulting: http://www.starling-so...

Jesús Gabriel y Galán

1/24/2008 8:25:00 AM

0

On Jan 22, 2008 11:29 PM, matt neuburg <matt@tidbits.com> wrote:
> Justin Collins <justincollins@ucla.edu> wrote:
>
> > irb(main):001:0> puts "#{hi}" if false
> > => nil
> > irb(main):002:0> puts "#{hi}" if hi = "hello"
> > NameError: undefined local variable or method `hi' for main:Object
> > from (irb):2
> > from :0
>
> Even weirder! Why does it work in the first case but not in the second.
> If you're going to say that 'hi' is undefined, surely it was undefined
> in line 001. Yet line 001 raised no error.
>
> So what's wrong with 002? It cannot be the 'puts "#{hi}"' part - you
> just proved, with 001, that that part's okay. So apparently it is the
> assignment part. Evidently a tailgating "if" refuses to auto-instantiate
> a variable. That is what I'm attempting to get clear on. Is that a bug?
> Is it expected behavior? m.

Because of how the parser works. It makes a first pass, and one of the
things it does is decide wheter tokens are methods or variables. The
way it does that is assuming everything is a method and if it finds an
assignment, from that point on, it treats it as a variable. The trick
is in the "from that point on", since for the parser it's just left to
right as it finds the code, it doesn't take into account evaluation
order (please anyone correct me if I'm wrong). Check this:

irb(main):001:0> def hi; "hi"; end
=> nil
irb(main):002:0> puts "#{hi}" if (hi = "hello")
(irb):2: warning: found = in conditional, should be ==
hi
=> nil
irb(main):003:0> puts hi
hello
=> nil

As you can see, I define a method hi. The parser will treat the first
hi in line 002 as a method, since it hasn't seen an assignment yet.
That's why line 002 outputs "hi", because the first reference has been
marked by the parser as a method call. After the hi = "hello", though,
hi is treated as a variable. That's why line 003 outputs "hello".

Hope this makes it a bit clearer, although I agree that this looks confusing.

Jesus.

7stud --

1/24/2008 9:38:00 AM

0

Jesús Gabriel y Galán wrote:
> On Jan 22, 2008 11:29 PM, matt neuburg <matt@tidbits.com> wrote:
>> If you're going to say that 'hi' is undefined, surely it was undefined
>> in line 001. Yet line 001 raised no error.
>>
>> So what's wrong with 002? It cannot be the 'puts "#{hi}"' part - you
>> just proved, with 001, that that part's okay. So apparently it is the
>> assignment part. Evidently a tailgating "if" refuses to auto-instantiate
>> a variable. That is what I'm attempting to get clear on. Is that a bug?
>> Is it expected behavior? m.
>
> Because of how the parser works. It makes a first pass, and one of the
> things it does is decide wheter tokens are methods or variables. The
> way it does that is assuming everything is a method and if it finds an
> assignment, from that point on, it treats it as a variable. The trick
> is in the "from that point on", since for the parser it's just left to
> right as it finds the code, it doesn't take into account evaluation
> order (please anyone correct me if I'm wrong). Check this:
>
> irb(main):001:0> def hi; "hi"; end
> => nil
> irb(main):002:0> puts "#{hi}" if (hi = "hello")
> (irb):2: warning: found = in conditional, should be ==
> hi
> => nil
> irb(main):003:0> puts hi
> hello
> => nil
>
> As you can see, I define a method hi. The parser will treat the first
> hi in line 002 as a method, since it hasn't seen an assignment yet.
> That's why line 002 outputs "hi", because the first reference has been
> marked by the parser as a method call. After the hi = "hello", though,
> hi is treated as a variable. That's why line 003 outputs "hello".
>
> Hope this makes it a bit clearer, although I agree that this looks
> confusing.
>
> Jesus.

So the parser marks hi as a method before the code hi="hello" is parsed,
and thereafter hi is marked as a variable? Then execution evaluates
hi="hello" and thereafter execution moves *backwards* to execute puts
hi? And in order to evaluate hi in the puts statement, execution
checks the parser's values, and at that point in the code hi is a
method, so the hi method executes and the return value is output?
--
Posted via http://www.ruby-....

Jesús Gabriel y Galán

1/24/2008 4:13:00 PM

0

On Jan 24, 2008 10:38 AM, 7stud -- <bbxx789_05ss@yahoo.com> wrote:
> Jes=FAs Gabriel y Gal=E1n wrote:
> > On Jan 22, 2008 11:29 PM, matt neuburg <matt@tidbits.com> wrote:
>
> >> If you're going to say that 'hi' is undefined, surely it was undefined
> >> in line 001. Yet line 001 raised no error.
> >>
> >> So what's wrong with 002? It cannot be the 'puts "#{hi}"' part - you
> >> just proved, with 001, that that part's okay. So apparently it is the
> >> assignment part. Evidently a tailgating "if" refuses to auto-instantia=
te
> >> a variable. That is what I'm attempting to get clear on. Is that a bug=
?
> >> Is it expected behavior? m.
> >
> > Because of how the parser works. It makes a first pass, and one of the
> > things it does is decide wheter tokens are methods or variables. The
> > way it does that is assuming everything is a method and if it finds an
> > assignment, from that point on, it treats it as a variable. The trick
> > is in the "from that point on", since for the parser it's just left to
> > right as it finds the code, it doesn't take into account evaluation
> > order (please anyone correct me if I'm wrong). Check this:
> >
> > irb(main):001:0> def hi; "hi"; end
> > =3D> nil
> > irb(main):002:0> puts "#{hi}" if (hi =3D "hello")
> > (irb):2: warning: found =3D in conditional, should be =3D=3D
> > hi
> > =3D> nil
> > irb(main):003:0> puts hi
> > hello
> > =3D> nil
> >
> > As you can see, I define a method hi. The parser will treat the first
> > hi in line 002 as a method, since it hasn't seen an assignment yet.
> > That's why line 002 outputs "hi", because the first reference has been
> > marked by the parser as a method call. After the hi =3D "hello", though=
,
> > hi is treated as a variable. That's why line 003 outputs "hello".
> >
> > Hope this makes it a bit clearer, although I agree that this looks
> > confusing.
> >
> > Jesus.
>
> So the parser marks hi as a method before the code hi=3D"hello" is parsed=
,
> and thereafter hi is marked as a variable? Then execution evaluates
> hi=3D"hello" and thereafter execution moves *backwards* to execute puts
> hi? And in order to evaluate hi in the puts statement, execution
> checks the parser's values, and at that point in the code hi is a
> method, so the hi method executes and the return value is output?

Sorry, I don't have enough knowledge to answer your question. What I
explained and showed above is my understanding of how this issue works
from the functional point of view, but I have no idea of how the
parser and the execution internals are in this regard. Maybe some
other person might explain it.

Jesus.

Gary Wright

1/24/2008 5:11:00 PM

0


On Jan 24, 2008, at 4:38 AM, 7stud -- wrote:
>
> So the parser marks hi as a method before the code hi="hello" is
> parsed,
> and thereafter hi is marked as a variable? Then execution evaluates
> hi="hello" and thereafter execution moves *backwards* to execute puts
> hi?

Execution isn't moving backwards. You have to think of it as a two
step process, parsing then execution. The parser is going to prepare:

puts "#{hi}" if (hi = "hello")
puts hi

is such a way that the execution step sees:

if (hi = "hello")
puts "#{hi}"
end
puts hi

So during execution, nothing is going backwards. But the parser has
already tagged various parts of the code such that the 'hi' token in
the conditional is an local variable assignment from a literal (thus
the warning), the 'hi' token in the then clause as a method call, and
the 'hi' token on the final puts as a local variable.

These three issues
1) 0-argument method calls and local variables
are syntactically identical
2) local variables don't have to be declared.
3) Ruby's if/unless modifiers imply execution
order that is backwards from textual order

when considered separately are quite nice, but when combined, have
some awkward corner cases. You could get rid of the problem by:

a) forcing all 0-arg method calls to be self.method or method()
b) requiring temporary variables to be declared
c) eliminating if/unless modifiers
d) implementing more elaborate multi-pass parser