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
如果你运行这样的功能会发生什么? –