What is difference in below usage
a = proc { puts 'hii' }
def abc(&a)
a.call
end
abc(&a)
def xyz(c)
c.call
end
xyz(a)
In below implementation more than one blocks can be passed as arguments -
def pqr(c, &t)
c.call
yield
xyz(c)
abc(&t)
end
pqr(a) { puts 'block to method'}
In the first of the two, the &a parameter will also capture a block passed like this:
abc {puts "Hello world"}
This is the same as:
xyz(proc {puts "Hello world"})
The other of the two only allows the last of the two.
Related
def test(args,&block)
yield
end
test 1, {puts "hello"}
Last line doesn't work. How do I pass a block with other arguments?
test(1){ puts "hello" }
or
test(1) do
puts "hello"
end
or
blk = proc{ puts "hello" }
test(1, &blk)
You can check out this https://pine.fm/LearnToProgram/chap_10.html
As #Cary Swoveland suggested we can go slightly deeper.
Any Ruby method can implicitly accept a block. And even though you didn't define it in your method signature you still can capture it and pass further.
So, considering this idea we can do following manipulations with your method:
def test(args, &block)
yield
end
is the same as
def test(args)
yield
end
and the same as
def test(args)
block = Proc.new
block.call
end
When you have this implicit block capturing you'd probably want to add extra check:
def test(args)
if block_given?
block = Proc.new
block.call
else
"no block"
end
end
or
def test(args)
if block_given?
yield
else
"no block"
end
end
So calling these methods will return following:
test("args")
#=> no block
test("args"){ "Hello World" }
#=> "Hello World"
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
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
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