Blocks as Closures: Using Outside Variables Within a Code Block

Blocks as Closures Using Outside Variables Within a Code Block

Problem

You want to share variables between a method, and a code block defined within it.

Solution

Just reference the variables, and Ruby will do the right thing. Here's a method that adds a certain number to every element of an array:

def add_to_all(array, number) array.collect { |x| x + number } end add_to_all([1, 2, 3], 10) # => [11, 12, 13]

Enumerable#collect can't access number directly, but it's passed a block that can access it, since number was in scope when the block was defined.

Discussion

A Ruby block is a closure: it carries around the context in which it was defined. This is useful because it lets you define a block as though it were part of your normal code, then tear it off and send it to a predefined piece of code for processing.

A Ruby block contains references to the variable bindings, not copies of the values. If the variable changes later, the block will have access to the new value:

tax_percent = 6 position = lambda do "I have always supported a #{tax_percent}% tax on imported limes." end position.call # => "I have always supported a 6% tax on imported limes." tax_percent = 7.25 position.call # => "I have always supported a 7.25% tax on imported limes."

This works both ways: you can rebind or modify a variable from within a block.

counter = 0 4.times { counter += 1; puts "Counter now #{counter}"} # Counter now 1 # Counter now 2 # Counter now 3 # Counter now 4 counter # => 4

This is especially useful when you want to simulate inject or collect in conjunction with a strange iterator. You can create a storage object outside the block, and add things to it from within the block. This code simulates Enumerable#collect, but it collects the elements of an array in reverse order:

accumulator = [] [1, 2, 3].reverse_each { |x| accumulator << x + 1 } accumulator # => [4, 3, 2]

The accumulator variable is not within the scope of Array#reverse_each, but it is within the scope of the block.

Категории