How to wrap a method that yields in Ruby 1.9 - ruby

I have a method that prints out a numbered list, yielding to a code block to print a prefix.
arr = %w(a b c)
def print_lines(array)
array.each_with_index do |item, index|
prefix = yield index
puts "#{prefix} #{item}"
end
end
print_lines(arr) do |index|
"(#{index})"
end
This produces the following output:
(0) a
(1) b
(2) c
Now I want to wrap print_lines in another method and call it.
def print_lines_wrapped(array)
puts 'print_lines_wrapped'
print_lines(array)
end
print_lines_wrapped(arr) do |index|
"(#{index})"
end
However, this gives me a LocalJumpError
test_yield.rb:5:in `block in print_lines': no block given (yield) (LocalJumpError)
from test_yield.rb:4:in `each'
from test_yield.rb:4:in `each_with_index'
from test_yield.rb:4:in `print_lines'
from test_yield.rb:16:in `print_lines_wrapped'
from test_yield.rb:19:in `<main>'
Why do I get a LocalJumpError?
How can I implement print_lines_wrapped such that I can call it like this:
print_lines_wrapped(arr) do |index|
"(#{index})"
end
and get the following output:
print_lines_wrapped
(0) a
(1) b
(2) c
?

Your wrapper method also has to accept a block and pass it to the wrapped method. There is no implicit passing of the block:
def print_lines_wrapped(array, &block)
puts 'print_lines_wrapped'
print_lines(array, &block)
end
Example:
def asdf(&block) puts yield(2) end
def qwer(&block)
puts "I am going to call asdf"
asdf &block
end
asdf { |x| x * 3 }
6
=> nil
qwer { |x| x * 5 }
I am going to call asdf
10
=> nil
The & operator converts its operand into a block if possible
qwer &Proc.new { |x| x * 2 }
I am going to call asdf
4

Related

Ruby: instance_exec with a block taking a block as parameter

Please consider the following code:
def a &block_a
define_method :b do |&block_b|
puts "Running in define_method"
instance_exec block_a, &block_b
end
end
a do
puts "In block A"
end
b do |&block|
puts "In block B"
instance_eval &block
end
I'd like the block given to method 'b' to be given as argument the block given to method 'a' (b being created dynamically).
However the following output/error is returned:
Running in define_method
In block B
wrong number of arguments (given 0, expected 1..3)
(repl):14:in `instance_eval'
(repl):14:in `block in <main>'
(repl):4:in `instance_exec'
(repl):4:in `block in a'
(repl):12:in `<main>'
It seems like the '&block' is nil (trying to call 'block.call' instead of 'instance_eval &block' raises 'block is nil' error). The logic seems to be good since the replacing the 'b' block argument with a string does work:
def a s
define_method :b do |&block_b|
puts "Running in define_method"
instance_exec s, &block_b
end
end
a "LOL"
b do |s|
puts "In block B"
puts s
end
Thanks for your help !
It is incredibly hard to understand what you are trying to accomplish and I am 102% positive you are overcomplicating things (read: doing it wrong.)
The issue with your code is you are lost in blocks and their parameters.
For block in b method call to be not nil, b should yield something to it, and yielding blocks is not permitted. Instead of yielding, one might explicitly call the block:
def a &block_a
define_method :b do |&block_b|
# ⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓⇓
block_b.(&block_a)
end
end
a { puts "A" }
b { |&block| puts "B"; instance_eval &block }
other way round might be to accept a normal parameter in b, since what you pass there is block_a, which is a normal proc instance:
def a &block_a
define_method :b do |&block_b|
instance_exec block_a, &block_b
end
end
a { puts "A" }
# ⇓⇓⇓⇓⇓⇓⇓
b { |block| puts "B"; instance_eval &block }

When to use method { } vs method(&block)?

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

what's main use of yield method in ruby? [duplicate]

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

Trouble yielding inside a block/lambda

I have the following Ruby code:
# func1 generates a sequence of items derived from x
# func2 does something with the items generated by func1
def test(x, func1, func2)
func1.call(x) do | y |
func2.call(y)
end
end
func1 = lambda do | x |
for i in 1 .. 5
yield x * i
end
end
func2 = lambda do | y |
puts y
end
test(2, func1, func2) # Should print '2', '4', '6', '8', and '10'
This does not work, of course.
test.rb:11: no block given (LocalJumpError)
from test.rb:10:in `each'
from test.rb:10
from test.rb:4:in `call'
from test.rb:4:in `test'
from test.rb:20
Lambdas don't implicitly accept blocks like regular methods do, so your func1 can't yield. Do this instead:
func1 = lambda do |x, &blk|
for i in 1 .. 5
blk.call(x * i)
end
end
Specifically, I believe this is because yield would send control back to the caller's block, which would not include lambda invocations. So the following code works like you "expect":
def foo
(lambda { |n| yield(n) }).call(5)
end
foo { |f| puts f } # prints 5
In Ruby 1.9 only:
func1 = lambda do |x, &blk|
for i in 1..5
blk.call(x*i)
end
end
def test(x, func1, func2)
func1.call(x) do | y |
func2.call(y)
end
end
#change func1 to a method
def func1 x
for i in 1 .. 5
yield x * i
end
end
#func2 may be either a method or a lambda
#I changed it for consistency, but you don't have to
def func2 y
puts y
end
test(2, method(:func1), method(:func2))
Based upon Nikita Misharin's answer here:[https://stackoverflow.com/a/45571976/2165560], I like this:
def iterator(x)
for i in 1 .. 5
yield x * i
end
end
iteratorWrapper = -> (m,&block) { iterator(m) {|n| block.call n} }
iteratorWrapper.call(2) { |y| puts y }
It answers my question here [In Ruby, can you use the lambda or or Proc call method to invoke an iterator?.
By wrapping the iterator it can be arbitrarily passed to other methods and iterate on their blocks.

What does it mean to yield within a block?

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

Resources