def any?
if block_given?
method_missing(:any?) { |*block_args| yield(*block_args) }
else
!empty?
end
end
In this code from ActiveRecord, what is the purpose of a yield statement that exists within a block?
Basically if the current method has been given a code-block (by the caller, when it was invoked), the yield executes the code block passing in the specified parameters.
[1,2,3,4,5].each { |x| puts x }
Now { |x| puts x} is the code-block (x is a parameter) passed to the each method of Array. The Array#each implementation would iterate over itself and call your block multiple times with x = each_element
pseudocode
def each
#iterate over yourself
yield( current_element )
end
Hence it results
1
2
3
4
5
The *block_args is a Ruby way to accept an unknown number of parameters as an array. The caller can pass in blocks with different number of arguments.
Finally let's see what yield within a block does.
class MyClass
def print_table(array, &block)
array.each{|x| yield x}
end
end
MyClass.new.print_table( [1,2,3,4,5] ) { |array_element|
10.times{|i| puts "#{i} x #{array_element} = #{i*array_element}" }
puts "-----END OF TABLE----"
}
Here Array#each yields each element to the block given to MyClass#print_table...
It does not mean anything special. It's just a yield like any other yield.
def test_method
["a", "b", "c"].map {|i| yield(i) }
end
p test_method {|i| i.upcase }
# => ["A", "B", "C"]
In the code snippet from active record, the purpose is to yield every time the block of any? is called.
This helped me understand: yield is a way to insert blocks into a method you already wrote, which means "execute something here". For instance,
Without parameter
def original_method
puts "a"
puts "b"
end
What if you want to execute some unknown block between those two p?
def updated_method
puts "a"
yield
puts "b"
end
# these two are the same:
updated_method { puts "execute something here" }
updated_method do puts "execute something here" end
The result would be
a
execute something here
b
You can have as many yields in a method as you like.
With parameter
def updated_method
puts "a"
yield
puts "b"
end
What if you want to execute some unknown block on the string "Execute something on me!"?
def updated_method_with_argument
puts "a"
yield("Execute something on me!")
puts "b"
end
updated_method_with_argument do |argument|
puts "This argument gets put out: " << argument
end
The result would be
a
This argument gets put out: Execute something on me!
b
Related
When we want to pass blocks to a method, when do we want to do:
block = Proc.new { puts 'test blocks & procs' }
def method(&block)
yield
end
VS
def method
yield
end
method { puts 'test blocks & procs' }
Is there any particular circumstance we would want to use one or the other?
Using Procs lets you not repeat yourself. Compare this:
arr1 = [1,2,3]
arr2 = [4,5,6]
Using blocks, you repeat the block twice:
arr1.map { |n| n * 2 }
arr2.map { |n| n * 2 }
When using Procs, you can reuse the object:
multiply_2 = Proc.new do |n|
n * 2
end
arr1.map(&multiply_2)
arr2.map(&multiply_2)
1) A block is not an object, and therefore a block cannot be captured in a variable, and a block cannot be passed explicitly to a method. However, you can convert a block to a Proc instance using the & operator, and a Proc instance is an object which can be assigned to a variable and passed to a method.
2) Proc.new() doesn't return a block--it returns a Proc instance. So naming your variable block is misleading.
3) yield only calls a block, which is the thing specified after a method call:
do_stuff(10) {puts 'hello'} #<-- block
do_stuf(10) do |x| #<--'do' marks the start of a block
puts x + 2
end #<--end of block
block = Proc.new {puts 'hello'}
^
|
+--- #not a block
yield does not call a Proc instance that happens to be passed as an argument to a method:
def do_stuff(arg)
yield
end
p = Proc.new { puts 'test blocks & procs' }
do_stuff(p)
--output:--
1.rb:2:in `do_stuff': no block given (yield) (LocalJumpError)
from 1.rb:6:in `<main>'
Compare to:
def do_stuff(arg)
puts arg
yield
end
do_stuff(10) {puts "I'm a block"}
--output:--
10
I'm a block
4) You can convert a block to a Proc instance and capture it in a variable using &, like this:
def do_stuff(arg, &p)
puts arg
p.call
end
do_stuff(10) {puts "I'm a block"}
--output:--
10
I'm a block
Liar! You are not a block anymore! Typically, the variable name used to capture a block is written as &block:
def do_stuff(arg, &block)
puts arg
block.call
end
But that's technically incorrect; the block variable will contain a Proc instance, as you can see here:
def do_stuff(arg, &block)
puts arg
puts block.class
end
do_stuff(10) {puts "I'm a block"}
--output:--
10
Proc
5) You can also use the & operator to convert a Proc instance to a block, as Nobita's answer demonstrated:
def do_stuff(x, y)
yield(x, y)
end
p = Proc.new {|x, y| puts x+y}
do_stuff(10, 20, &p)
--output:--
30
In that example, the method call do_stuff(10, 20, &p) is equivalent to writing:
do_stuff(10, 20) {|x, y| puts x+y}
6) When do you want to use a block and yield v. &block and call?
One use case for capturing a block in a variable is so that you can pass it to another method:
def do_stuff(arg, &a_proc)
result = arg * 2
do_other_stuff(result, a_proc)
end
def do_other_stuff(x, p)
1.upto(x) do |i|
p[i] #Proc#[] is a synonym for Proc#call
end
end
do_stuff(2) {|x| puts x}
--output:--
1
2
3
4
I suggest you operate by these two rules:
When you write a method that expects a block, ALWAY execute the block with yield.
If #1 doesn't work for you, consider capturing the block and using call (or [])
Block seems to be a bit faster based on the following benchmark:
require 'benchmark/ips'
prc = Proc.new { '' }
def _proc(&block)
yield
end
def _block
yield
end
Benchmark.ips do |x|
x.report('Block') { _block { '' } }
x.report('Proc') { _proc(&prc) }
end
Benchmark results on an i7-4510U CPU # 2.00GHz
Block 149.700k i/100ms
Proc 144.151k i/100ms
Block 4.786M (± 1.6%) i/s - 23.952M
Proc 4.269M (± 2.3%) i/s - 21.334M
Say I have the following code:
def a(n, m, &block)
yield if block_given?
end
def a
# My question is here. When a is called, block might be or might not be
# given. Below line is obvious wrong. How to call b and properly pass
# block to b?
b(1, 2, &block)
end
a # call a without block
a { # call a with a block
puts "in block"
}
Write a() to accept a block. It is implied to be optional, and as Andrew Marshall noted, will be passed along as &nil if not given.
def b(n, m, &block)
yield if block_given?
puts "no block" if !block_given?
end
def a( &block )
b(1, 2, &block)
end
a # call a without block
a { # call a with a block
puts "in block"
}
Output:
no block
in block
Could anyone please help me to understand the difference between "yield self" and "yield"?
class YieldFirstLast
attr_accessor :first, :last
def initialize(first = nil, last = nil)
#first = first
#last = last
yield self if block_given?
end
def hello
puts "#{#first} #{#last} says hello!"
end
end
In the case of yield self, self is the argument passed to the block. With simply yield, no argument is passed. self is not special here, anything could be yielded, e.g.
class Foo
def a() yield self end
def b() yield end
def c() yield "Bar" end
def d() yield 1, 2, "scuba" end
def to_s() "A!" end
end
Foo.new.a {|x| puts x } #=> A!
Foo.new.b {|x| puts x } #=> (a blank line, nil was yielded)
Foo.new.c {|x| puts x } #=> Bar
Foo.new.d {|x, y, z| puts z } #=> scuba
yield self enters block, associated with method call, passing current object as argument to the block, plain yield just enters the block without passing any arguments.
Think of yield as invoking your block and yield self is invoking your block with the current instance as the parameter.
Assuming I have the following proc:
a = Proc.new do
puts "start"
yield
puts "end"
end
Also assuming I pass a to another method which subsequently calls instance_eval on another class with that block, how can I now pass a block onto the end of that method which gets yielded in a.
For example:
def do_something(a,&b)
AnotherClass.instance_eval(&a) # how can I pass b to a here?
end
a = Proc.new do
puts "start"
yield
puts "end"
end
do_something(a) do
puts "this block is b!"
end
Output should of course should be:
start
this block is b!
end
How can I pass the secondary block to a in the instance_eval?
I need something like this for the basis of a Ruby templating system I'm working on.
You can't use yield in a. Rather, you have to pass a Proc object. This would be the new code:
def do_something(a,&b)
AnotherClass.instance_exec(b, &a)
end
a = Proc.new do |b|
puts "start"
b.call
puts "end"
end
do_something(a) do
puts "this block is b!"
end
yield is only for methods. In this new code, I used instance_exec (new in Ruby 1.9) which allows you to pass parameters to the block. Because of that, we can pass the Proc object b as a parameter to a, which can call it with Proc#call().
a=Proc.new do |b|
puts "start"
b.call
puts "end"
end
def do_something(a,&b)
AnotherClass.instance_eval { a.call(b) }
end
def any?
if block_given?
method_missing(:any?) { |*block_args| yield(*block_args) }
else
!empty?
end
end
In this code from ActiveRecord, what is the purpose of a yield statement that exists within a block?
Basically if the current method has been given a code-block (by the caller, when it was invoked), the yield executes the code block passing in the specified parameters.
[1,2,3,4,5].each { |x| puts x }
Now { |x| puts x} is the code-block (x is a parameter) passed to the each method of Array. The Array#each implementation would iterate over itself and call your block multiple times with x = each_element
pseudocode
def each
#iterate over yourself
yield( current_element )
end
Hence it results
1
2
3
4
5
The *block_args is a Ruby way to accept an unknown number of parameters as an array. The caller can pass in blocks with different number of arguments.
Finally let's see what yield within a block does.
class MyClass
def print_table(array, &block)
array.each{|x| yield x}
end
end
MyClass.new.print_table( [1,2,3,4,5] ) { |array_element|
10.times{|i| puts "#{i} x #{array_element} = #{i*array_element}" }
puts "-----END OF TABLE----"
}
Here Array#each yields each element to the block given to MyClass#print_table...
It does not mean anything special. It's just a yield like any other yield.
def test_method
["a", "b", "c"].map {|i| yield(i) }
end
p test_method {|i| i.upcase }
# => ["A", "B", "C"]
In the code snippet from active record, the purpose is to yield every time the block of any? is called.
This helped me understand: yield is a way to insert blocks into a method you already wrote, which means "execute something here". For instance,
Without parameter
def original_method
puts "a"
puts "b"
end
What if you want to execute some unknown block between those two p?
def updated_method
puts "a"
yield
puts "b"
end
# these two are the same:
updated_method { puts "execute something here" }
updated_method do puts "execute something here" end
The result would be
a
execute something here
b
You can have as many yields in a method as you like.
With parameter
def updated_method
puts "a"
yield
puts "b"
end
What if you want to execute some unknown block on the string "Execute something on me!"?
def updated_method_with_argument
puts "a"
yield("Execute something on me!")
puts "b"
end
updated_method_with_argument do |argument|
puts "This argument gets put out: " << argument
end
The result would be
a
This argument gets put out: Execute something on me!
b