简体   繁体   English

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

[英]recursive stack overflow with exceptions in F#

let's look at that code:让我们看看这段代码:

let rec doSomething () =
    let d = GetSomeDataFromSomewhere()
    match d with
    | Some x -> x
    | None   -> doSomething()

so that's some form of non stop polling..所以这是某种形式的不间断轮询..

but now the following form:但现在是以下形式:

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

that one will lead to a stack overflow if there are a lot of exceptions.如果有很多异常,那将导致堆栈溢出。

Can someone explain the mechanics at play that make the two versions behave differently?有人可以解释一下使这两个版本表现不同的机制吗?

The issue is that the first call in your second version is not in a tail-call position. This is not entirely obvious, because the recursive call is the "last thing the function does", but the runtime still has to keep the stack frame around, because it needs to keep the associated exception handler.问题是第二个版本中的第一个调用不在尾调用 position 中。这并不完全明显,因为递归调用是“function 所做的最后一件事”,但运行时仍然必须保留堆栈帧around,因为它需要保留关联的异常处理程序。

let rec doSomething () =
    try        
        let d = GetSomeDataFromSomewhereButItCouldCrash()
        match d with
        | Some x -> x
        | None   -> doSomething() // This is not a tail call!
    with _ ->
        doSomething() // This is a tail call

If you handle exceptions directly when calling GetSomeDataFromSomewhere and turn them into None , then you can keep the same logic, but make it tail recursive:如果您在调用GetSomeDataFromSomewhere时直接处理异常并将它们变成None ,那么您可以保持相同的逻辑,但使其尾部递归:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM