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: 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
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