2010-05-14 162 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