开发者

Yielding in an anonymous block

开发者 https://www.devze.com 2023-03-18 21:29 出处:网络
I\'m not making sense of the following behavior (see also in this SO thread): def def_test puts \'def_test.in\'

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.

0

精彩评论

暂无评论...
验证码 换一张
取 消