使用“返回”;在 Ruby 块中
我正在尝试使用 Ruby 1.9.1 作为嵌入式脚本语言,以便“最终用户”代码可以在 Ruby 块中编写。这样做的一个问题是,我希望用户能够在块中使用“return”关键字,这样他们就不必担心隐式返回值。考虑到这一点,这就是我希望能够做的事情:
def thing(*args, &block)
value = block.call
puts "value=#{value}"
end
thing {
return 6 * 7
}
如果我在上面的示例中使用“return”,我会得到一个 LocalJumpError。我知道这是因为有问题的块是 Proc 而不是 lambda。如果我删除“return”,该代码就可以工作,但我真的更希望能够在这种情况下使用“return”。这可能吗?我尝试将块转换为 lambda,但结果是相同的。
I'm trying to use Ruby 1.9.1 for an embedded scripting language, so that "end-user" code gets written in a Ruby block. One issue with this is that I'd like the users to be able to use the 'return' keyword in the blocks, so they don't need to worry about implicit return values. With this in mind, this is the kind of thing I'd like to be able to do:
def thing(*args, &block)
value = block.call
puts "value=#{value}"
end
thing {
return 6 * 7
}
If I use 'return' in the above example, I get a LocalJumpError. I'm aware that this is because the block in question is a Proc and not a lambda. The code works if I remove 'return', but I'd really prefer to be able to use 'return' in this scenario. Is this possible? I've tried converting the block to a lambda, but the result is the same.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
只需在这种情况下使用
next
即可:return
总是从方法返回,但是如果您在 irb 中测试此代码片段,则没有方法,这就是为什么您有LocalJumpError
break
从块返回值并结束其调用。如果您的块是由yield
或.call
调用的,则break
也会从此迭代器中断next
返回值阻止并结束其调用。如果您的块是由yield
或.call
调用的,则next
将值返回到调用yield
的行Simply use
next
in this context:return
always returns from method, but if you test this snippet in irb you don't have method, that's why you haveLocalJumpError
break
returns value from block and ends its call. If your block was called byyield
or.call
, thenbreak
breaks from this iterator toonext
returns value from block and ends its call. If your block was called byyield
or.call
, thennext
returns value to line whereyield
was called在 Ruby 中你不能这样做。
return
关键字总是从当前上下文中的方法或 lambda 返回。在块中,它将从定义闭包的方法返回。它无法从调用方法或 lambda 中返回。Rubyspec 表明这确实是 Ruby 的正确行为(诚然,这不是一个真正的实现,但目标是与 C Ruby 完全兼容):
You cannot do that in Ruby.
The
return
keyword always returns from the method or lambda in the current context. In blocks, it will return from the method in which the closure was defined. It cannot be made to return from the calling method or lambda.The Rubyspec demonstrates that this is indeed the correct behaviour for Ruby (admittedly not a real implementation, but aims full compatibility with C Ruby):
我很欣赏 s12chung 的答案。这是我对他的答案的一点改进。它可以避免方法
__thing
使上下文混乱。I admire the answer of s12chung. Here is my little improvement of his answer. It lets avoid cluttering the context with method
__thing
.你从错误的角度看待它。
这是一个
thing
的问题,而不是 lambda 的问题。You are looking it from the wrong point of view.
This is an issue of
thing
, not the lambda.我在用 ruby 为 Web 框架编写 DSL 时遇到了同样的问题...(Web 框架 Anorexic 会摇滚!)...
无论如何,我深入研究了 ruby 内部结构并找到了一个简单的解决方案,使用 Proc 调用时返回的 LocalJumpError返回...到目前为止,它在测试中运行良好,但我不确定它是否完全证明:
救援部分中的 if 语句可能看起来像这样:
但这对我来说是未知的领域,所以我会坚持下去到目前为止我测试过的。
I had the same issue writing a DSL for a web framework in ruby... (the web framework Anorexic will rock!)...
anyway, I dug into the ruby internals and found a simple solution using the LocalJumpError returned when a Proc calls return... it runs well in the tests so far, but I'm not sure it's full-proof:
the if statement in the rescue segment could probably look something like this:
but it's uncharted territory for me, so I'll stick to what I tested so far.
我找到了一种方法,但它涉及定义一个方法作为中间步骤:
I found a way, but it involves defining a method as an intermediate step:
事物在哪里被调用?你在班级里吗?
您可以考虑使用这样的东西:
Where is thing invoked? Are you inside a class?
You may consider using something like this:
我相信这是正确的答案,尽管有缺点:
这个 hack 允许用户在他们的过程中使用 return 而不会产生任何后果, self 被保留,等等。
在这里使用 Thread 的优点是,在某些情况下你不会得到 LocalJumpError -并且返回将发生在最意想不到的地方(在顶级方法上,意外地跳过其主体的其余部分)。
主要缺点是潜在的开销(如果您的场景足够的话,您可以仅使用
yield
替换 Thread+join)。I believe this is the correct answer, despite the drawbacks:
This hack allows users to use return in their procs without consequences, self is preserved, etc.
The advantage of using Thread here is that in some cases you won't get the LocalJumpError - and the return will happen in the most unexpected place (onside a top-level method, unexpectedly skipping the rest of it's body).
The main disadvantage is the potential overhead (you can replace the Thread+join with just the
yield
if that's enough in your scenario).