简体   繁体   English

为什么无限递归不会在 F# 中遇到堆栈溢出异常?

[英]Why doesn't infinite recursion hit a stack overflow exception in F#?

I know this is somewhat the reverse of the issue people are having when they ask about a stack overflow issue, but if I create a function and call it as follows, I never receive any errors, and the application simply grinds up a core of my CPU until I force-quit it:我知道这与人们在询问堆栈溢出问题时遇到的问题有些相反,但是如果我创建一个函数并按如下方式调用它,我永远不会收到任何错误,并且该应用程序只是磨碎了我的核心CPU,直到我强制退出它:

let rec recursionTest x =
    recursionTest x

recursionTest 1

Of course I can change this out so it actually does something like this:当然我可以改变它,所以它实际上是这样的:

let rec recursionTest (x: uint64) =
    recursionTest (x + 1UL)

recursionTest 0UL

This way I can occasionally put a breakpoint in my code and see the value of x is going up rather quickly, but it still doesn't complain.这样我可以偶尔在我的代码中放置一个断点,并看到 x 的值上升得相当快,但它仍然没有抱怨。 Does F# not mind infinite recursion? F# 不介意无限递归吗?

Your recursionTest function is tail recursive, which means all recursive calls occurs in the 'tail position' ie as the last action in the function.您的recursionTest函数是尾递归的,这意味着所有递归调用都发生在“尾位置”,即作为函数中的最后一个动作。 This means the F# compiler does not need to allocate a new stack frame for recursive calls, so no stack overflow occurs.这意味着 F# 编译器不需要为递归调用分配新的堆栈帧,因此不会发生堆栈溢出。

Tail recursion is a specific case of tail call , the tail call being to itself rather than some other function.尾递归是尾调用的一种特殊情况,尾调用是针对自身而不是其他函数。

In general, F# emits the tailcall instruction that .NET honors:通常,F# 发出 .NET 认可的尾调用指令:

http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.tailcall(v=vs.95).aspx http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.tailcall(v=vs.95).aspx

In some specific simple cases, like yours, F# rewrites programs that use recursion into equivalent programs using loops.在某些特定的简单情况下,例如您的情况,F# 将使用递归的程序重写为使用循环的等效程序。

This is an example of tail call optimization and so the compiler is optimizing it into a simple loop.这是尾调用优化的一个示例,因此编译器将其优化为一个简单的循环。 See this: https://devblogs.microsoft.com/fsharpteam/tail-calls-in-f/请参阅: https : //devblogs.microsoft.com/fsharpteam/tail-calls-in-f/

Try something like this:尝试这样的事情:

let rec recursionTest x =
    recursionTest x + recursionTest (x * 2)

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

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