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