Sabahat
Sabahat

Reputation: 63

Execution flow of times iterator and for loop in ruby

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 or each) 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

Answers (3)

Stefan
Stefan

Reputation: 114178

This is somehow expected. According to the documentation: (emphasis added)

The for loop is similar to using each, 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

user1351104
user1351104

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

rdubya
rdubya

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

Related Questions