【问题标题】:Yielding in an anonymous block在匿名区块中产生
【发布时间】:2011-07-07 15:45:33
【问题描述】:

我无法理解以下行为(另请参阅 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 变量并直接调用它,但我更希望了解为什么我最终需要这样做。

【问题讨论】:

标签: ruby


【解决方案1】:

block_given? 考虑def 范围,而不是lambda 范围:

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

test { puts "In block" }

【讨论】:

  • 抱歉,这不是我希望的答案。我的问题是为什么l.call { puts "In block" } 不工作,而不是test { puts "In block" }。如果你编辑你的函数来调用l.call &Proc.new,我最初的印象是它会起作用(因为它会使用正确定义的函数);但事实并非如此,我想了解原因。
  • @Denis 这取决于您所说的“工作”。 yieldblock_given? 将始终在 def 范围内工作,lambda 范围对两者都是透明的。将块传递给lambda 不会改变它。您可以使用通过lambda do |&f| 参数传递给lambda 的块,但不能使用yieldblock_given?lambdadef 不是一回事。
  • 是的,它最终“打勾”,但它只是在阅读了 mu 的答案后才这样做。我回想起来,你的答案在哪些方面确实有效。只是,对于像我这样的 ruby​​ 新手来说,mu 更冗长的回答让正在发生的事情更清楚。
【解决方案2】:

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 不占用块。

这并不完全是一个权威的答案,但也许它会澄清一点。

【讨论】:

  • 我想说它比您想象的要权威得多:两个小时的谷歌搜索并没有产生更多的结果。代表下一位遇到此线程的新红宝石爱好者感谢您!
  • @Denis:如果我可以指出一些官方文档说“lambdas 捕获块以及其他所有内容”,我认为它是权威的,我猜Proc#call 文档结合了“隐藏的@ 987654337@ 参数”参数非常接近。谷歌搜索常见的 Ruby 事情确实会产生(哈哈)挫败感,但随着 ruby​​-doc.org 和 apidock.com 的排名上升,情况正在变得更好。
猜你喜欢
  • 1970-01-01
  • 2015-11-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-29
  • 1970-01-01
  • 2014-09-30
相关资源
最近更新 更多