I'm not making sense of the following behavior (see also in this SO thread):
def def_test
puts 'def_test.in'
yield if block_given?
puts 'def_test.out'
end
def_test do
puts 'def_test ok'
end
block_test = proc do |&block|
puts 'block_test.in'
block.call if block
puts 'block_test.out'
end
block_test.call do
puts 'block_test'
end
proc_test = proc do
puts 'proc_test.in'
yield if block_given?
puts 'proc_test.out'
end
proc_test.call do
puts 'proc_test ok'
end
Output:
def_test.in
def_test ok
def_test.开发者_如何学JAVAout
block_test.in
block_test ok
block_test.out
proc_test.in
proc_test.out
I don't mind resorting to explicitly declaring the &block variable and calling it directly, but I'd more ideally like to make some sense of why I end up needing to.
block_given?
considers def
scope, not lambda
scope:
def test
l = lambda do
yield if block_given?
end
l.call
end
test { puts "In block" }
The lambda
is a closure and it seems to be capturing the block_given?
and block from its outer scope. This behavior does make sense as the block is, more or less, an implied argument to the outer method; you can even capture the block in a named argument if desired:
def def_test(&block)
frobnicate &block
end
So the block is part of the argument list even when it isn't named.
Consider this simple piece of code:
def f
lambda do
puts "\tbefore block"
yield if block_given?
puts "\tafter block"
end
end
puts 'Calling f w/o block'
x = f; x.call
puts
puts 'Calling f w/ block'
x = f { puts "\t\tf-block" }; x.call
puts
puts 'Calling f w/o block but x with block'
x = f; x.call { puts "\t\tx-block" }
puts
puts 'Calling f w/ block and x with block'
x = f { puts "\t\tf-block" }; x.call { puts "\t\tx-block" }
This produces the following for me with 1.9.2:
Calling f w/o block
before block
after block
Calling f w/ block
before block
f-block
after block
Calling f w/o block but x with block
before block
after block
Calling f w/ block and x with block
before block
f-block
after block
Furthermore, Proc#call
(AKA proc ===
) doesn't take a block:
prc === obj → result_of_proc
Invokes the block, with obj as the block‘s parameter. It is to allow a proc object to be a target of when clause in the case statement.
Compare the first line with the documentation for Enumerable#chunk
(for example):
enum.chunk {|elt| ... } → an_enumerator
The {...}
indicates that chunk
is documented to take a block, the lack of such notation for Proc#call
indicates that Proc#call
does not take a block.
This isn't exactly an authoritative answer but maybe it clears things up a little bit.
精彩评论