屈服于一个匿名的块

时间:2011-07-07 15:45:34

标签: ruby

我没有理解以下行为(另见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

输出:

def_test.in
def_test ok
def_test.out
block_test.in
block_test ok
block_test.out
proc_test.in
proc_test.out

我不介意明确声明& block变量并直接调用它,但我更理想的是要理解为什么我最终需要。

2 个答案:

答案 0 :(得分:5)

block_given?考虑def范围,而非lambda范围:

def test
  l = lambda do
    yield if block_given?
  end
  l.call
end

test { puts "In block" }

答案 1 :(得分:4)

lambda是一个闭包,它似乎捕获block_given?并阻止其外部范围。这种行为确实有意义,因为块或多或少是外部方法的隐含参数;如果需要,您甚至可以在命名参数中捕获块:

def def_test(&block)
    frobnicate &block
end

因此,即使没有命名,该块也是参数列表的一部分。

考虑这段简单的代码:

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" }

这为我提供了以下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

此外,Proc#call(AKA proc ===)不会阻止:

  

prc === obj→result_of_proc
  调用块,使用obj作为块的参数。它允许proc对象成为case语句中when子句的目标。

将第一行与Enumerable#chunk的文档进行比较(例如):

  

enum.chunk {| elt | ......}→an_enumerator

{...}表示chunk被记录为阻止,Proc#call缺少此表示法表示Proc#call没有阻止。

这不是一个权威性的答案,但也许它可以解决一些问题。