Ruby Variable Scope and Blocks

The blocks used by iterators (such as loop and each) are a little different. Normally, the local variables created in these blocks are not accessible outside the block. [ 1, 2, 3 ].each do |x| y = x + 1 end [ x, y ]

produces: prog.rb:4: undefined local variable or method `x’ for # (NameError) However, if at the time the block executes a local variable [that] already exists with the same name as that of a variable in the block, the existing local variable will be used in the block. Its value will therefore be available after the block finishes.

My first impression was that it was wierd for blocks to have access to variables outside the block. But after working with the language, I realized that was perfectly natural and consistent with blocks becoming closures. And that the wierd thing was that local variables created in the block were not accessible outside the block. For example, it’s easy to find the sum or product of an array of numbers when the block has access to variables that have been previously defined:

1
irb(main):001:0> sum = 0
=> 0
irb(main):002:0> (1..10).each { |i| sum += i }
=> 1..10
irb(main):003:0> sum
=> 55

And it’s wierd that this:

1
irb(main):001:0> if false then last = nil end
=> nil
irb(main):002:0> (1..10).each { |i| last = i }
=> 1..10
irb(main):003:0> last
=> 10

behaves differently than this:

1
irb(main):001:0> (1..10).each { |i| last = i }
=> 1..10
irb(main):002:0> last
NameError: undefined local variable or method `last' for main:Object
        from (irb):2

The good thing is that it jumps up and bites you right away.

22 Mar: inject, inject, inject – OK, summing values was a bad example. I still maintain that having access to local variables outside the block is a better fit to the language.