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.
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?
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!"
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: 10You 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
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.
[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