8
我一直在谷歌搜索了很長時間,仍然無法找到答案。根據我的理解,如果調用者已將調用封裝在try/catch和/或try/finally塊中,則在.NET 4.5上運行的F#3.0將不會使用遞歸方法的尾遞歸。如果嘗試/捕捉或嘗試/最終在堆棧上的幾個級別是什麼情況?F中的尾遞歸和異常#
我一直在谷歌搜索了很長時間,仍然無法找到答案。根據我的理解,如果調用者已將調用封裝在try/catch和/或try/finally塊中,則在.NET 4.5上運行的F#3.0將不會使用遞歸方法的尾遞歸。如果嘗試/捕捉或嘗試/最終在堆棧上的幾個級別是什麼情況?F中的尾遞歸和異常#
如果你包的一些(尾)身體遞歸函數在try
... with
塊則函數不是尾遞歸了,因爲調用幀不能遞歸調用過程中被丟棄 - 它需要留在該堆棧具有註冊的異常處理程序。
例如,假設您有類似iter
功能List
:
let rec iter f list =
try
match list with
| [] ->()
| x::xs -> f x; iter f xs
with e ->
printfn "Failed: %s" e.Message
當你調用iter f [1;2;3]
那麼它會創建一個異常處理4個嵌套堆棧幀(如果你添加rethrow
到with
分支,那麼它實際上會打印錯誤消息4次)。
您不能真正添加異常處理程序而不會中斷尾遞歸。但是,您通常不需要嵌套的異常處理程序。所以最好的解決辦法是重寫功能,使得它不需要在每一個遞歸調用來處理異常:
let iter f list =
let rec loop list =
match list with
| [] ->()
| x::xs -> f x; loop xs
try loop list
with e -> printfn "Failed: %s" e.Message
這有一點點不同的意義 - 但它不會產生嵌套的異常處理程序和loop
尚可完全尾遞歸。
另一種選擇是僅在主體上添加異常處理,不包括尾遞歸調用。實際上,在這個例子中唯一可以拋出異常的是對f
的調用;
let rec iter f list =
match list with
| [] ->()
| x::xs ->
try
f x
with e ->
printfn "Failed: %s" e.Message
iter f xs
如果你運行這樣的功能會發生什麼? –