Reputation: 63
In ruby we have a times iterator and I have read in some blog that
Variables declared in blocks passed to iterators (e.g.,
times
oreach
) are undefined at the top of each iteration!
so when I try to execute
3.times do |loop_num|
sum ||= 0
sum += 1
puts sum
end
it gives me output
1
1
1
[Finished in 1.1s]
and if I do same with for loop
for loop_num in 1..3
sum ||= 0
sum += 1
puts sum
end
Output is
1
2
3
[Finished in 0.2s]
but now when I again execute the times iterator it is giving me output as
4
5
6
[Finished in 0.2s]
So can any one explain this behavior, why it is not assigning the sum with 0 instead taking its value as 3(which is last value assigned to sum in for loop) and giving results as 4 5 6
Upvotes: 3
Views: 174
Reputation: 114178
This is somehow expected. According to the documentation: (emphasis added)
The
for
loop is similar to usingeach
, but does not create a new variable scope.
In Ruby, regular methods like times
, loop
or each
do create a new variable scope, whereas control expressions like for
, while
or until
do not.
Therefore, this code:
for loop_num in 1..3
sum ||= 0
sum += 1
puts sum
end
3.times do
sum ||= 0
sum += 1
puts sum
end
Is equivalent to:
sum ||= 0
sum += 1
puts sum
sum ||= 0
sum += 1
puts sum
sum ||= 0
sum += 1
puts sum
3.times do
sum ||= 0
sum += 1
puts sum
end
Upvotes: 3
Reputation:
Building upon @rdubya's answer, you're misinterpreting the blog post quote.
What the post means is that in this code block:
3.times do |t|
puts defined?(var_only_in_block) ? true : false
var_only_in_block = 1
end
# false
# false
# false
each iteration destroys the variable var_only_in_block
after running, which makes it undefined at the start of the next iteration.
However, blocks have access to variables in the scope they were defined in. Take this example:
var_outside_block = 1
3.times do |t|
var_outside_block += 1
puts var_outside_block
end
# => 2
# => 3
# => 4
Because the variable was defined outside of the block, changes made during each iteration are preserved.
Upvotes: 1
Reputation: 2916
The reason you are seeing this behavior is because 3.times
is executing a block while the for .. in
loop is executing in the same context it is called. This is where closures come into play.
In your first example, sum
hasn't been defined anywhere that the block can access it. Therefore, sum
is re-defined each time the block is executed. If you add a puts sum
statement after the block, you will see that sum
is undefined.
For the for loop example, you stay in the same context so the second time through the loop sum
has already been defined in the context so it uses the existing value. You can verify this by adding a puts sum
statement after the loop and you will see that it has the last value that it was set to in the loop.
When you execute your 3.times
block after you have executed your loop, sum
exists in the closure so the block uses the already defined variable instead of re-defining it each time through the loop.
Upvotes: 3