【问题标题】:Why does the break statement in ruby behave differently when using Proc.new v. the ampersand sign?为什么在使用 Proc.new v. & 符号时,ruby 中的 break 语句表现不同?
【发布时间】:2012-02-11 22:08:14
【问题描述】:

块的break语句(根据The Ruby Programming Language)定义如下:

它使块返回到它的迭代器,而迭代器返回到调用它的方法。

因此,当运行以下代码时,会导致 LocalJumpError。

def test
    puts "entering test method"
    proc = Proc.new { puts "entering proc"; break }
    proc.call # LocalJumpError: iterator has already returned
    puts "exiting test method"
end
test

虽然以下代码不会抛出 LocalJumpError。 & 符号有什么特别之处? & 符号不是隐式使用 Proc.new 吗?

def iterator(&proc)
    puts "entering iterator"
    proc.call # invoke the proc
    puts "exiting iterator" # Never executed if the proc breaks
end

def test
    iterator { puts "entering proc"; break }
end
test

换句话说,我将 & 符号作为内联 Proc.new 调用的一种方式。此时的行为应该与第一个代码 sn-p 相同。

def iterator (p = Proc.new { puts "entering proc"; break})
...
end

免责声明:我是新手学习这门语言(ruby 1.9.2),因此希望得到参考和详细的概要。

【问题讨论】:

  • 没有时间给出正确的答案,但这都是关于范围的,而不是 Proc 或 Lambda 的特殊性。
  • 当您有时间...请再次访问此问题。我会很感激你的洞察力
  • 代替Proc.new试试lambda
  • 这是一个关于语言的一般问题;在实现中我会使用 lambda

标签: ruby-on-rails ruby proc-object


【解决方案1】:

break 使块成为块返回的调用者。在以下代码中:

proc = Proc.new { break }

转换为 Proc 对象的块的“调用者”是 Proc.new。 break 应该让块的调用者返回,但是 Proc.new 已经返回了。

在这段代码中:

def iterator(&b); b.call; end
iterator { break }

块的调用者是iterator,所以它使iterator返回。

【讨论】:

  • 知道了...这是最好的答案。但是,不是所有的块都通过 Proc.new 转换为可操作的代码吗?因此迭代器 (&b) 不会变成迭代器 (b = Proc.new b) 吗?
  • & 和 Proc.new 不一样。 & 是核心语法; Proc.new 是一个库方法。您可以像这样编写自己的 Proc.new:class Proc; def self.new(&b); b; end; end。但是您无法实现自己的核心语法(除了破解解释器或使用预处理器)。
【解决方案2】:

这是answer

& 用于将 proc 转换为块,将块转换为 proc。

我更改了示例以与您的情况相关:

def run_my_code(&my_code)
 puts 'before proc'
 my_code.call
 puts 'after proc'
end
run_my_code { puts "passing a block, accepting a proc"; break}
=> before proc
   passing a block, accepting a proc

正如你所看到的,它没有到达'after proc'

def run_my_code
 yield
end
my_proc = Proc.new  { puts "passing a proc instead of block"; break}
run_my_code &my_proc
=> passing a proc instead of block
   LocalJumpError: break from proc-closure
   from (pry):75:in `block in <main>'

在您的第二个示例中,结果中有一个 proc,该 proc 从 iterator 中断并返回到 test 函数。

def iterator(&proc)
  puts 'entering iterator'
  proc.call
  puts 'exiting iterator'
end

def test
  puts 'before test'
  iterator { puts 'entering proc'; break }
  puts 'after test'
end

=>before test
entering iterator
entering proc
after test

【讨论】:

  • 如果我有这个正确的,在第一个示例中,break 语句从 Proc.new (因为那是一个迭代器)和包含它的块返回。据我了解,我在问题开头记录了定义,为什么第一个失败。但是,如果在第二个示例中,& 符号是 Proc.new 的语法糖,那么我不明白,为什么它不会在那里失败
  • 那么,如果一个块已经被转换为一个proc,那么break语句不应该和第一个代码sn-p一样工作吗?我的意思是,如果我们基本上是内联 Proc.new,为什么它的行为与位于方法调用上方的单独行中的 Proc.new 不同?
  • "proc 从迭代器中中断并返回到测试函数。"如果这是真的,那么迭代器的以下定义不应该失败,而是会失败:def iterator (p = Proc.new { puts "entering proc"; break})。我能想到的只是 Proc.new 是一个迭代器和 & 符号,不使用 yield 语句来创建 proc。
  • 是的,我的解释不正确。现在我认为这是因为'break'关键字而不是proc。但是找不到这个关键字的完整描述。
【解决方案3】:

这与块、proc 和 lambda 之间的差异以及它们各自的作用域有关。

我在 2009 年写了一篇关于它的帖子,您可能会觉得有用:http://www.leonardoborges.com/writings/2009/07/22/procs-lambdas-blocks-whats-the-difference/

希望这会有所帮助。

【讨论】:

  • 这篇文章显然很有见地,但鉴于我在这两种情况下都使用了一个 proc,我不确定为什么 Ruby 试图对一个 proc 给予特殊处理而不是另一个?
  • 这与帖子中的return关键字解释有关。 break,如本例中的 return 表示从调用方法中中断,本例中为 test。但是,您不能中断测试,因为您可以通过在 Proc.new {...} 之后放置一个中断来验证,这在您的第二个 sn-p 上是不同的,因为它将从调用方法返回,在这种情况下是迭代器,将控制权返回给新的测试版本。
  • 那么如果 test 被包装在一个名为 test_test 的方法中,那么 Proc.new 中的 break 语句应该不会失败?这里'我如何读取块到 proc 转换 def iterator(&proc); #implicity call proc = Proc.new {proc}; ...end 在这种情况下,LocalJumpError 应该发生,就像在测试中一样。
  • 它确实隐含地调用它,但不是在iterator(&amp;proc)。该方法获取传入的 Proc,而不是内联创建它。
  • 那么为什么以下失败 def iterator(proc) ...end iterator Proc.new { puts "entering proc";打破}
猜你喜欢
  • 1970-01-01
  • 2015-04-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-14
  • 2017-05-13
相关资源
最近更新 更多