tags:

views:

108

answers:

2
+1  Q: 

Closures in Ruby

I'm kind of new to Ruby and some of the closure logic has me a confused. Consider this code:

array = []
for i in (1..5)
  array << lambda {j}
end
array.map{|f| f.call} # => [5, 5, 5, 5, 5]

This makes sense to me because i is bound outside the loop, so the same variable is captured by each trip through the loop. It also makes sense to me that using an each block can fix this:

array = []
(1..5).each{|i|  array << lambda {i}}
array.map{|f| f.call} # => [1, 2, 3, 4, 5]

...because i is now being declared separately for each time through. But now I get lost: why can't I also fix it by introducing an intermediate variable?

array = []
for i in 1..5
  j = i
  array << lambda {j}
end
array.map{|f| f.call} # => [5, 5, 5, 5, 5]

Because j is new each time through the loop, I'd think a different variable would be captured on each pass. For example, this is definitely how C# works, and how -- I think-- Lisp behaves with a let. But in Ruby not so much. It almost looks like = is aliasing the variable instead of copying the reference, but that's just speculation on my part. What's really happening?

Edit: See comments in the answers; the problem seems to be that j is still in scope outside the loop. How does scope in loops really work?

Edit: I guess I still don't understand; if loops don't create new scopes, why this:

for i in 1..5
  puts j if i > 1 #undefined local variable or method `j' for main:Object (NameError)
  j = i
end
+1  A: 

What version of Ruby are you running this on? 1.8 does not have block scope for local variables, so j is still hanging around (and equal to 5) even after the end of the for.

x1a4
1.9.1. When exploring this, I'd put a `puts j` above `j=i` and got an error, so I'd ruled out j's scope as the issue. But I just tried `puts j` at the very end, and it does print 5, so something is up there. So maybe the real question is, how does j's scope work here?
Isaac Cambron
er, I mean `puts j if i > 1`, or else I'd get an error regardless
Isaac Cambron
+7  A: 
Jörg W Mittag
Thanks, almost there. See my new edit
Isaac Cambron
It's good enough for me. Ruby sure is quirky.
Isaac Cambron