问题描述
我一直在使用Google的年龄,但仍无法找到答案。从我的理解F#3.0其他计算机上的.NET 4.5,如果调用已包装的调用在一个try / catch和/或尝试了递归方法将不使用尾递归/ finally块。这是什么情况,如果有一个try / catch或尝试/最后几级堆栈?
如果您在尝试
... <$包裹的一些(尾)身体递归函数C $ C>与块,然后该功能不是尾递归了,因为调用帧不能递归调用过程中被丢弃 - 它需要留在堆栈注册的异常处理程序。
举例来说,假设你有类似 ITER
功能列表
:
让REC ITER˚F列表=
尝试
匹配列表
| [] - &GT; ()
| X :: XS - &GT; ˚FX; ITER˚FXS
随e - &GT;
printfn失败:%s的e.Message
当你调用 ITER F [1; 2; 3]
然后它会创建4异常处理程序嵌套堆栈帧(如果您添加重新抛出
到与
分支,那么它实际上将打印错误消息4倍)。
您不能真正增加的异常处理程序不破坏尾递归。但是,您通常不需要嵌套的异常处理程序。所以最好的解决方法是重写功能,使得它并不需要处理的每递归调用异常:
让ITER˚F列表=
让REC循环列表=
匹配列表
| [] - &GT; ()
| X :: XS - &GT; ˚FX;环XS
尝试循环列表
随e - &GT; printfn失败:%s的e.Message
这有一点不同的意义 - 但它不创建嵌套的异常处理程序和循环
也可以完全尾递归。
另一种选择是增加异常处理只在身体的不包括的尾递归调用。实际上,能在这个例子中抛出异常的唯一的事情为f 调用;
让REC ITER˚F列表=
匹配列表
| [] - &GT; ()
| X :: XS - &GT;
尝试
˚Fx
随e - &GT;
printfn失败:%s的e.Message
ITER˚FXS
I've been googling for ages and still can't find the answer. From what I understand F# 3.0 runnning on .NET 4.5 will not use tail recursion for a recursive method if the invoker has wrapped the call in a try/catch and/or try/finally block. What is the situation if there is a try/catch or try/finally a few levels up the stack?
If you wrap the body of some (tail) recursive function in a try
... with
block then the function is not tail recursive anymore, because the call frame cannot be discarded during the recursive call - it needs to stay in the stack with a registered exception handler.
For example, say you have something like iter
function for List
:
let rec iter f list =
try
match list with
| [] -> ()
| x::xs -> f x; iter f xs
with e ->
printfn "Failed: %s" e.Message
When you call iter f [1;2;3]
then it will create 4 nested stack frames with exception handlers (and if you added rethrow
into the with
branch, then it would actually print the error message 4 times).
You cannot really add exception handlers without breaking tail-recursion. However, you do not usually need nested exception handlers. So the best solution is to rewrite the function so that it does not need to handle exceptions in every recursive call:
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
This has a little different meaning - but it does not create nested exception handlers and loop
can still be fully tail recursive.
Another option would be to add exception handling only over the body excluding the tail-recursive call. Realistically, the only thing that can throw an exception in this example is the call to f
;
let rec iter f list =
match list with
| [] -> ()
| x::xs ->
try
f x
with e ->
printfn "Failed: %s" e.Message
iter f xs
这篇关于尾递归和F#中的异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!