为啥显式返回会对 Proc 产生影响?
Posted
技术标签:
【中文标题】为啥显式返回会对 Proc 产生影响?【英文标题】:Why does explicit return make a difference in a Proc?为什么显式返回会对 Proc 产生影响? 【发布时间】:2010-11-28 23:24:57 【问题描述】:def foo
f = Proc.new return "return from foo from inside proc"
f.call # control leaves foo here
return "return from foo"
end
def bar
b = Proc.new "return from bar from inside proc"
b.call # control leaves bar here
return "return from bar"
end
puts foo # prints "return from foo from inside proc"
puts bar # prints "return from bar"
我认为 return
关键字在 Ruby 中是可选的,并且无论您是否请求,您始终是 return
ing。鉴于此,我发现 foo
和 bar
具有不同的输出,这是由 foo
在 Proc f
中包含显式 return
这一事实决定的,这让我感到惊讶。
有人知道为什么会这样吗?
【问题讨论】:
【参考方案1】:Ruby 具有三个构造:
-
block 不是对象,由
...
或do
... end
创建。
proc 是由Proc.new
或proc
创建的Proc
对象。
lambda 是由lambda
(或Ruby 1.8 中的proc
)创建的Proc
。
Ruby 有三个从某事物返回的关键字:
return
终止它所在的方法或 lambda。
next
终止它所在的块、proc 或 lambda。
break
终止产生块或调用它所在的 proc 或 lambda 的方法。
在 lambdas 中,return
的行为类似于 next
,无论出于何种原因。 next
和 break
之所以如此命名,是因为它们最常与 each
之类的方法一起使用,其中终止块将导致迭代以集合的 next 元素继续, 并且终止 each
将使您中断退出循环。
如果您在
foo
的定义中使用return
,您将从foo
返回,即使它位于块或过程中。要从块返回,您可以改用 next
关键字。
def foo
f = Proc.new next "return from foo from inside proc"
f.call # control leaves foo here
return "return from foo"
end
puts foo # prints "return from foo"
【讨论】:
编辑也提到了 lambdas 的行为 注意,在你的例子中,如果 next 被省略,行为仍然存在。 顺便说一句,我认为我们都很好地回答了一个不同的问题 :) 至于为什么,我认为只有 Matz 知道,很多关于闭包的东西都违反了最小意外原则。跨度> 感谢您在这里的彻底回答。真的很清楚。 感谢您用很好的例子进行解释【参考方案2】:这是Proc
s 的语义;它不一定是所有块的语义。我同意这有点令人困惑。它的存在是为了增加灵活性(可能部分原因是 Ruby 除了实现之外没有规范)。
行为在Proc
实现中定义。 Lambda
s 的行为不同,因此如果您希望 return
s 不退出 退出封闭方法,请使用 lambdas。或者,从 Proc
中省略 return
关键字。
深入研究 Rubys 闭包is here。这是一个很棒的曝光。
所以:
def foo
f = Proc.new
p2 = Proc.new return "inner proc";
p2.call
return "proc"
f.call
return "foo"
end
def foo2
result = Proc.new"proc".call
"foo2 (proc result is: #result)"
end
def bar
l = lambda return "lambda"
result = l.call
return "bar (lambda result is: #result)"
end
puts foo
# inner proc
puts foo2
# foo (proc result is: proc)
puts bar
# bar (lambda result is: lambda)
【讨论】:
"这是 Procs 的语义,不一定是所有块的语义。"它是 Proc.new 创建的 Proc 实例以及所有“普通”块(即不与proc
或 lambda
关键字一起使用的块)的语义。
没错,我可以添加一个示例,但我认为我的示例已经足够复杂了。
由于某种原因,转换为块的 lambda 就像它们总是块一样:[1].map &lambdareturn "lambda"
从函数返回。这是 JRuby 中的错误吗?
puts foo2
将返回 foo2 (proc result is: proc)
【参考方案3】:
这样想:Proc.new 只是创建一个代码块,它是调用函数的一部分。 proc/lambda 创建一个具有特殊绑定的匿名函数。一些代码示例会有所帮助:
def foo
f = Proc.new return "return from foo from inside Proc.new"
f.call # control leaves foo here
return "return from foo"
end
等价于
def foo
begin
return "return from foo from inside begin/end"
end
return "return from foo"
end
所以很明显返回只会从函数'foo'返回
相反:
def foo
f = proc return "return from foo from inside proc"
f.call # control stasy in foo here
return "return from foo"
end
等价于(忽略此示例中未使用的绑定):
def unonymous_proc
return "return from foo from inside proc"
end
def foo
unonymous_proc()
return "return from foo"
end
显然不会从 foo 返回,而是继续下一条语句。
【讨论】:
尽管这是一个老问题,但请注意 Ruby 1.8.7 和 1.9.3 之间存在差异,后者 Kernel.proc 的行为类似于 Proc.new 而不是 lambda。以上是关于为啥显式返回会对 Proc 产生影响?的主要内容,如果未能解决你的问题,请参考以下文章
为啥从 main() 显式返回 0 被认为是好的做法? [复制]