Recursion F中带异常的递归堆栈溢出#

Recursion F中带异常的递归堆栈溢出#,recursion,exception,f#,Recursion,Exception,F#,让我们看一下代码: let rec doSomething () = let d = GetSomeDataFromSomewhere() match d with | Some x -> x | None -> doSomething() 这是某种形式的不间断投票 但现在是以下形式: let rec doSomething () = try let d = GetSomeDataFromSomewhere

让我们看一下代码:

let rec doSomething () =
    let d = GetSomeDataFromSomewhere()
    match d with
    | Some x -> x
    | None   -> doSomething()
这是某种形式的不间断投票

但现在是以下形式:

let rec doSomething () =
    try        
        let d = GetSomeDataFromSomewhereButItCouldCrash()
        match d with
        | Some x -> x
        | None   -> doSomething()
    with _ ->
        doSomething()
如果存在大量异常,那么这将导致堆栈溢出


有人能解释一下导致两个版本行为不同的机制吗?

问题是,第二个版本中的第一个调用不在尾部调用位置。这一点并不十分明显,因为递归调用是“函数所做的最后一件事”,但运行时仍然需要保留堆栈框架,因为它需要保留关联的异常处理程序

let rec doSomething()=
尝试
设d=GetSomeDataFromSomeWherebuttCouldCrash()
匹配
|一些x->x
|None->doSomething()//这不是尾部调用!
与u->
doSomething()//这是一个尾部调用
如果在调用
getsomedatafromwhere
时直接处理异常,并将其转换为
None
,则可以保持相同的逻辑,但要使其尾部递归:

let rec doSomething () =
  let d = try GetSomeDataFromSomewhereButItCouldCrash() with _ -> None
  match d with
  | Some x -> x
  | None   -> doSomething()

为什么您认为第二个选项会导致堆栈溢出?我在一些调试代码中做了这样的循环,它会定期溢出,但非异常版本不会溢出。我可能错了,溢出不是来自异常本身吗?测试似乎表明了这一点,但我使用Rider作为IDE,它将我从调试器中踢出,因此我看不到详细信息。您可以尝试从
try
下移动
匹配项吗?@FyodorSoikin,我刚刚尝试:无堆栈溢出;我做了GetSomeData。。在try块内调用,在外部调用匹配。为什么会有不同的行为?这一定是因为在
try
yes中尾部调用不能是尾部调用,这是Fyodor在评论中的建议。这确实是正在发生的事情,但还不是很清楚。这可能是编译器警告的好地方。