2010-12-06 80 views
1

我讀this blog和整個下面的代碼異常會清除堆棧跟蹤什麼?

RunAgain = Class.new(Exception) 
def fib(i, n = 1, result = 0) 
    if i == -1 
    result 
    else 
    raise RunAgain 
    end 
rescue RunAgain 
    i, n, result = i - 1, n + result, n 
    retry 
end 

好像上面的代碼工作,一旦引發異常,然後紅寶石必須清空整個堆棧跟蹤和更換與異常的堆棧來了。

我的理解正確嗎?

回答

2

這段代碼的工作方式實際上與堆棧跟蹤沒有任何關係。整個過程中堆棧中有兩個條目,分別爲fibfib的調用者。例外情況的提升對於您的問題來說是一種紅鯡魚 - 除了作爲retry行爲的演示之外,本例中沒有任何用處。

Ruby的retry類似於其他控制關鍵字,如nextbreakredoredo關鍵字表示重試當前循環或從頂部的塊。 retry關鍵字在救援中工作以重試引發異常的當前塊。

所以會發生什麼這裏是一些初始值設定爲inresult,一個基本情況進行檢查(i == -1),如果不滿意,我們更新值並從上重試。請注意,由於這些值是方法參數而不是局部變量,因此不會重新初始化。

小心,因爲斐波那契是遞歸的一個非常常見的例子(也是非常差的一個例子),不要把它誤認爲遞歸算法。 RunAgain像循環一樣提升和拯救函數,而不用重新調用函數或修改調用堆棧。

你的示例代碼等同於這兩種情況下

def fib(i) 
    n, result = 1, 0 
    (i+1).times { n, result = n + result, n } 
    result 
end 

注意,i只是一個計數器,僅此而已。我們運行代碼i+1次。還要注意,交換值的臨時變量的典型需求被ruby的多重賦值構造所取代。

+0

如果我要求fibonacci爲一個很大的值,那麼將會出現1000萬個異常。如果所有這些異常信息都保存在堆棧上,那麼堆棧跟蹤錯誤最終將被拋出。因此,我的問題是當下一個異常出現時堆棧跟蹤信息從堆棧中刪除。 – 2010-12-07 20:14:07