【问题标题】:Ruby Lambda vs. Proc LocalJumpErrorRuby Lambda 与 Proc LocalJumpError
【发布时间】:2011-11-17 06:06:00
【问题描述】:

Ruby 和 StackOverflow 新手在这里使用 Ruby 并遇到了我的第一个主要障碍。我很难理解 Procs 和 Lambdas。这是我正在使用的代码。

def procBuilder(message)
  Proc.new{ puts message; return}
end

def test
  puts "entering method"
  p = procBuilder("entering proc")
  p.call
  puts "exit method"
end

test

按照设计,这是为了抛出 LocalJumpError,但我不明白为什么。如果我不得不猜测这是做什么的,我猜它最初会在 p = procBuilder("entering proc") 运行时打印“entering proc”,然后在 p.call 上抛出一个错误,因为 p.call 没有传递任何字符串,但显然我错过了这两行之间发生的一些关键问题。我也不完全理解为什么这适用于 lambda 而不是 proc,但我想理解该错误也将解决该问题。

提前感谢您的澄清

【问题讨论】:

  • 如果我错了,请纠正我,但我认为这与return 声明有关。 proc 中的return 也会尝试从调用方法返回。所以p.call 尝试从test 以及procbuilder 返回

标签: ruby proc-object


【解决方案1】:

这是我给一个相关问题的answer。它谈到了一些关于 lambda vs proc 和 LocalJumpErrors 的内容。

在 proc 中,return 是一段特殊的语法,它从 proc 的 lexical 范围返回,而不是从 proc 本身返回。所以它试图从已经退出的procBuilder返回。

有几种方法可以解决这个问题:

  1. 根本不要使用return。 Ruby 会自行将控制权返回给 proc 的调用者。
  2. proc 更改为lambda,这符合您的预期。 Lambda 的行为类似于方法; procs 就像块一样。

至于你预期的错误,你不应该得到那个。 procBuilder 返回一个包含消息变量的过程。 proc 本身不需要任何参数。

编辑:回答您的其他问题。 proc 是一个闭包。它“捕获”了消息变量(procBuilder 中的局部变量),该变量在创建 proc 时就在范围内。 proc 现在可以在你的程序中漫游,其中隐藏了 message 变量,当你调用它时可以打印出来。唯一的麻烦是 return 语句,它有额外的要求,它的词法范围仍然是“活的”。

所有这一切的原因是这种行为在块中确实很有帮助。在这种情况下,它根本没有帮助,所以你应该只使用lambda,其中 return 意味着不那么疯狂。

关于 ruby​​ 闭包的非常棒的教程:http://innig.net/software/ruby/closures-in-ruby.rb

【讨论】:

  • 谢谢艾萨克,但我还是有点模糊。当您说“从 proc 调用的方法返回的特殊语法,而不是 proc 本身”时,您是在告诉我它跳转到我的测试方法的末尾和程序的末尾,对吗?为什么这是一个错误,而不仅仅是程序的结束?
  • 再次感谢您的帮助,但我仍在苦苦挣扎(相信我,我的头已经彻底撞到墙上了)。如果 proc 在它到达 p.call 之前已经运行并返回,因此测试方法已经返回,那么 p.call 如何能够将“正在输入 proc”打印到控制台?
  • 我编辑了一下。我必须修复一些原始答案,然后回答您的其他问题
  • 我想我或多或少得到了它,也许“LocalJumpError”这个词本身只是给我一个循环。感谢所有的帮助。
【解决方案2】:

proc 和方法或 lambda 之间的一个重要区别是它们处理 return 语句的方式。如果一个方法定义在另一个方法内部,那么内部方法中的return语句只从内部方法本身退出,然后外部方法继续执行。这同样适用于在 lambda 中定义 lambda、在方法中定义 lambda 或在 lambda 中定义方法。但是,当在方法中定义 proc 时,return 语句将从 proc 以及外部(封闭)方法中退出。示例:

def meditate
    puts "Adjusting posture…"
    p = Proc.new { puts "Ringing bell…"; return }
    p.call
    puts "Sitting still…"  # This is not executed
end

meditate

Output:
Adjusting posture…
Ringing bell…

注意方法的最后一行没有执行,因为 proc 中的 return 语句已经从 proc 和封闭方法中退出。

如果我们定义一个没有封闭(外部)方法的proc并使用return语句,它将抛出LocalJumpError。

p = Proc.new { puts "Ringing bell…"; return }
p.call
Output:
Ringing bell…
LocalJumpError: unexpected return

这是因为当在 proc 中到达 return 语句时,它不是从调用它的上下文返回,而是从定义它(proc)的范围返回。在以下示例中,发生 LocalJumpError 是因为 proc 试图从定义它的顶级环境返回。

def meditate p
    puts "Adjusting posture…"
    p.call
    puts "Sitting still…"  # This is not executed
end

p = Proc.new { puts "Ringing bell…"; return }

meditate p
Output:
Adjusting posture…
Ringing bell…
LocalJumpError: unexpected return

通常,在 proc 中使用 return 语句不是一个好主意。 Procs 通常在方法之间传递,如果定义 proc 的方法已经返回,它将抛出异常。在下面的示例中,我们可以删除 return 语句。但是,在某些情况下,我们实际上需要返回一些东西。在后者中,最好使用 lambda 而不是 proc。稍后我们将看到 lambda 以不同的方式处理返回语句,更像是方法。

下面是另一个涉及return语句的场景:

def zafu_factory
    # This method will return the following proc implicitly
    Proc.new { puts "Round black zafu"; return }
end

def meditate
    puts "Adjusting posture…"
    p = zafu_factory
    p.call
    puts "Sitting still…"  # This is not executed
end

meditate
Output:
Adjusting posture…
Round black zafu
LocalJumpError: unexpected return

刚刚发生了什么? zafu_factory 方法创建并隐式返回一个 proc。然后,proc 由 meditate 方法调用,当到达 proc 中的 return 语句时,它尝试从定义它的上下文(zafu_factory 方法)返回。但是,zafu_factory 已经返回了 proc,并且方法每次调用时只能返回一次。换句话说,由于 zafu_factory 方法在调用 proc 并尝试第二次返回时已经返回,因此引发了异常。

在这篇关于Procs and Lambdas: Closures in Ruby的博客文章中查看更多信息

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-02-26
    • 2010-09-06
    • 2012-07-24
    • 1970-01-01
    • 2010-12-16
    • 2018-12-28
    • 1970-01-01
    相关资源
    最近更新 更多