What should this print?

B

Bouquet

Hi,

I was expecting the code below to print 1, 1,
but I get 1, nil.

old = nil
[1,1].each do |n|
if n != old
x = 1
old = n
end
p x
end

Thanks.
 
K

Ken Bloom

Hi,

I was expecting the code below to print 1, 1, but I get 1, nil.

old = nil
[1,1].each do |n|
if n != old
x = 1
old = n
end
p x
end

After the first iteration, old is now 1. In the second iteration, n=1, so
1==1 and the code in if statement doesn't execute. Since x wasn't defined
outside of the block, it's created a new (or not) in each iteration, and
here it wasn't created, so its value when read (p x) is nil.

--Ken
 
B

Bouquet

Mark said:
x declared *inside* the each block at (4), *only* when the if condition
is true! The first iteration (the first 1 in [1,1] at (2)), n doesn't
equal old (n=1, old=nil) so the if condition is true. So x is initiated
to 1, old becomes 1.
p x #=> prints 1
Now, (and this is a newbies understanding), at the end of this first
iteration at (8) x is forgotten. ie not available during the second
iteration.

If we start the second iteration, n=1 (the second 1 in [1,1] at (2)
and old (which *is* still available because it was declared outside the
each block) = 1. So if n=1 and old=1, we would never enter the if
statement at (3) and x would not be initiated. Hence p as nil.

Thanks Mark for the explanation. I didn't realise that local
variables were reset every loop iteration; and not to undefined,
but to nil.

old = nil
x = nil
[1,1].each do |n|
if n != old
x = 1
old = n
end
p x
end

Yes, this version does what I want.

Thanks again.
 
B

Bouquet

Ken said:
After the first iteration, old is now 1. In the second iteration, n=1, so
1==1 and the code in if statement doesn't execute. Since x wasn't defined
outside of the block, it's created a new (or not) in each iteration, and
here it wasn't created, so its value when read (p x) is nil.

Thanks Ken.

It's different to not being created on that iteration, because I'd
then get "undefined local variable or method `x'", and the bug would
have flagged itself.
 
B

Boson

With more context:

It's interesting that these two product different output:

old = nil
[1,1].each do |n|
if n != old
x = 1
old = n
end
p x
end

old = nil
for n in [1,1]
if n != old
x = 1
old = n
end
p x
end
 
G

Gregory Seidman

With more context:

It's interesting that these two product different output:

old = nil
[1,1].each do |n|
if n != old
x = 1
old = n
end
p x
end

old = nil
for n in [1,1]
if n != old
x = 1
old = n
end
p x
end

It's not especially interesting. The block passed to each has a local
variable, x, which is nil in the second iteration because it has never been
set. The for loop does not create a new scope, thus the x variable retains
the value it was set to in the previous iteration, i.e. 1.

Amusingly, if you run the two in the same Ruby instance in the opposite
order (the for loop first), you'd see identical output because in that case
x would have been declared in the outer scope and the block would be using
that x by closure.

--Greg
 
B

Boson

Gregory said:
It's not especially interesting. The block passed to each has a local
variable, x, which is nil in the second iteration because it has never been
set. The for loop does not create a new scope, thus the x variable retains
the value it was set to in the previous iteration, i.e. 1.

Thanks Greg, I'll read up on the creation of scope.

I'd assumed that "for" was just syntactic sugar for "each".
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,289
Messages
2,571,450
Members
48,127
Latest member
svastipharmancrr

Latest Threads

Top