2010-05-14 129 views
5

我使用Ruby 1.8.7返回的东西。红宝石块和块

p = lambda { return 10;} 
def lab(block) 
    puts 'before' 
    puts block.call 
    puts 'after' 
end 
lab p 

以上代码的输出是

before 
10 
after 

我重构相同的代码到这个

def lab(&block) 
    puts 'before' 
    puts block.call 
    puts 'after' 
end 
lab { return 10; } 

现在我越来越LocalJumpError:意想不到的回报。

对我来说,这两个代码都在做同样的事情。是的,在第一种情况下,我通过了一个proc,而在第二种情况下,我通过了一个block。但&块将该块转换为proc。所以proc.call应该表现相同。

是的,我已经看到了这个帖子Using 'return' in a Ruby block

回答

8

当你与&块传递,你将其转换为PROC。重要的一点是一个proc和一个lambda不同(lambda实际上是proc的一个子类),特别是它们如何处理return。

所以你重构的代码实际上是等价的:

p = Proc.new { return 10;} 
def lab(block) 
    puts 'before' 
    puts block.call 
    puts 'after' 
end 
lab p 

也产生LocalJumpError。

原因如下:proc的返回从其词法范围返回,但lambda返回到其执行范围。所以,lambda返回到lab,传入它的proc返回到它声明的外部范围。本地跳转错误意味着它无处可去,因为没有封闭函数。

The Ruby Programming Language最好说它:

Procs have block-like behavior and lambdas have method-like behavior

你只需要保持跟踪你所使用的地方的。正如其他人所建议的,你需要做的就是从你的区块中删除return,并且事情将按预期工作。

5

return块内部将返回从块的方法,而不是从块。从阻止使用next返回(它的命名,那是因为与迭代器的方法,如eachmap从块返回基本上意味着跳到循环的下一次迭代)。

请注意,当返回值是块中最后一个计算的表达式时,根本不需要任何返回语句,即lab { 10 }将执行同样的操作。

0

{}块包括它给出的上下文,所以return试图从行lab { return 10; }返回。您可以通过将该行放在方法中,然后返回(即“之后”不打印)来实际完成此项工作(有时甚至以有用的方式)。

要将10更改为block.call,请省略return(或替代next)。

0

我想你只需要取消引用块传递给它之前:

foo = lambda { return 10 } 

def trace_block(&fn) 
    puts 'before calling fn' 
    puts fn.call 
    puts 'after caling fn' 
end 

trace_block(&foo) 

输出:

before calling fn 
    10 
    after caling fn 

更多信息:

+0

http://www.robertsosinski.com/2008/12/21/understanding-ruby-blocks-procs-and-lambdas/是一个非常好的写作。谢谢分享。 – 2010-05-14 20:45:05