Bloques/Blocks

Ruby Code blocks (called closures in other languages) are chunks of code between braces or between do..end that you can associate with method invocations, almost as if they were parameters.

A Ruby block is a way of grouping statements, and may appear only in the source adjacent to a method call;

the block is written starting on the same line as the method call's last parameter (or the closing parenthesis of the parameter list).

The Ruby standard is to use braces for single-line blocks and do..end for multi-line blocks.

yield

Any method can be called with a block as an implicit argument. Inside the method, you can call the block using the yield keyword with a value.

Blocks can have their own arguments. There are many methods in Ruby that iterate over a range of values.

1] pry(main)> a = (1..8).to_a
=> [1, 2, 3, 4, 5, 6, 7, 8]
[2] pry(main)> a.each { |x| puts x*x };
1
4
9
16
25
36
49
64

Most of these iterators are written in such a way as to be able to take a code block as part of their calling syntax.

The method can then yield control to the code block (i.e. execute the block) during execution as many times as is necessary for the iteration to complete (e.g. if we are iterating over array values, we can execute the block as many times as there are array values etc.).

Once you have created a block, you can associate it with a call to a method. Usually the code blocks passed into methods are anonymous objects, created on the spot.

If you provide a code block when you call a method, then inside the method, you can yield control to that code block - suspend execution of the method; execute the code in the block; and return control to the method body, right after the call to yield.

[~/local/src/ruby/rubytesting]$ cat block_simple.rb 
def call_block
  puts "Start of method" 
  yield
  yield
  puts "End of method"
end

[3] pry(main)> require_relative 'block_simple'
=> true
[4] pry(main)> call_block do puts "hello!" end 
Start of method
hello!
hello!
End of method
=> nil

If no code block is passed, Ruby raises an exception

~/rubytesting]$ pry
[1] pry(main)> require_relative 'block_simple'
=> true
[2] pry(main)> call_block
Start of method
LocalJumpError: no block given (yield)
from /Users/casiano/local/src/ruby/rubytesting/block_simple.rb:3:in `call_block'

You can provide parameters to the call to yield: these will be passed to the block. Within the block, you list the names of the arguments to receive the parameters between vertical bars (|).

[~/local/src/ruby/rubytesting]$ pry
[1] pry(main)> def tutu(x)
[1] pry(main)*   "hello #{ yield x }"
[1] pry(main)* end  
=> nil
[2] pry(main)> tutu('Juan') { |y| y+'a' }
=> "hello Juana"
[3] pry(main)>

Note that the code in the block is not executed at the time it is encountered by the Ruby interpreter. Instead, Ruby remembers the context in which the block appears and then enters the method.

[~/rubytesting]$ cat block_simple2.rb 
y = 4

def call_block(a)
  puts "Start of method" 
  puts "Inside the method a = #{a}"
  yield 4
  yield 5
  puts "End of method"
end

call_block 8 do |x|
    puts x+y
end
[~/rubytesting]$ ruby block_simple2.rb 
Start of method
Inside the method a = 8
8
9
End of method

block_given?

block_given? returns true if yield would execute a block in the current context.

[5] pry(main)> show-source tutu

From: (pry) @ line 3:
Owner: Object
Visibility: public
Number of lines: 4

def tutu
  return yield 4 if block_given?
  "no block was given!"
end
[6] pry(main)> tutu { |x| x*x }
=> 16
[7] pry(main)> tutu 
=> "no block was given!"

Ambito/Scope

Let us see what happens in the following example when a variable outside a block is x and a block parameter is also named x.

[~/rubytesting]$ cat block_scope1.rb 
x = 10  
5.times do |x|  
  puts "x inside the block: #{x}"  
end  
  
puts "x outside the block: #{x}"  
[~/rubytesting]$ ruby block_scope1.rb 
x inside the block: 0
x inside the block: 1
x inside the block: 2
x inside the block: 3
x inside the block: 4
x outside the block: 10
You will observe that after the block has executed, x outside the block is the original x. Hence the block parameter x was local to the block.

Since x is not a block parameter here, the variable x is the same inside and outside the block.

[~/rubytesting]$ cat block_scope2.rb 
x = 10  
5.times do |y|  
  x = y  
  puts "x inside the block: #{x}"  
end  
  
puts "x outside the block: #{x}"  
[~/rubytesting]$ ruby block_scope2.rb 
x inside the block: 0
x inside the block: 1
x inside the block: 2
x inside the block: 3
x inside the block: 4
x outside the block: 4

In Ruby 1.9, blocks introduce their own scope for the block parameters only. This is illustrated by the following example:

[~/rubytesting]$ cat block_scope3.rb  
x = 10  
5.times do |y, x|  
  x = y  
  puts "x inside the block: #{x}"  
end  
puts "x outside the block: #{x}"  
[~/rubytesting]$ ruby -v
ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-darwin13.0]
[~/rubytesting]$ ruby block_scope3.rb 
x inside the block: 0
x inside the block: 1
x inside the block: 2
x inside the block: 3
x inside the block: 4
x outside the block: 10
[~/rubytesting]$ rvm use 1.8.7-p352
Using /Users/casiano/.rvm/gems/ruby-1.8.7-p352
[~/rubytesting]$ ruby -v
ruby 1.8.7 (2011-06-30 patchlevel 352) [i686-darwin11.3.0]
[~/rubytesting]$ ruby block_scope3.rb 
x inside the block: 0
x inside the block: 1
x inside the block: 2
x inside the block: 3
x inside the block: 4
x outside the block: 4

Ambiguedad

Keep in mind that the braces syntax has a higher precedence than the do..end syntax.

Braces have a high precedence; do has a low precedence. If the method invocation has parameters that are not enclosed in parentheses, the brace form of a block will bind to the last parameter, not to the overall invocation.

7] pry(main)> a = [1,2,3]
=> [1, 2, 3]
[8] pry(main)> p a.map { |n| n*2 }
[2, 4, 6]
=> [2, 4, 6]
[9] pry(main)> p a.map do |n| n*2 end
#<Enumerator: [1, 2, 3]:map>

En el caso p a.map do |n| n*2 end la sentencia se interpreta como (p a.map) (do |n| n*2 end).

The do form will bind to the invocation.

El bloque se asocia con su método mas cercano

[10] pry(main)> show-source tutu

From: (pry) @ line 12:
Owner: Object
Visibility: public
Number of lines: 4

def tutu(s)
  puts "tutu"
  yield 4
end
[11] pry(main)> show-source titi

From: (pry) @ line 5:
Owner: Object
Visibility: public
Number of lines: 4

def titi
  puts "titi"
  yield 5
end
[12] pry(main)> tutu titi { |x| puts x } { |z| puts "2nd bloc #{z}" }
titi
5
tutu
2nd bloc 4
=> nil

Véase



Subsecciones
Casiano Rodriguez León 2015-01-07