我知道这与人们在询问堆栈溢出问题时遇到的问题有些相反,但如果我创建一个函数并按如下方式调用它,我从未收到任何错误,应用程序只是研磨出一个我的CPU的核心,直到我强制退出它:
let rec recursionTest x =
recursionTest x
recursionTest 1
当然我可以改变它,所以它实际上是这样的:
let rec recursionTest (x: uint64) =
recursionTest (x + 1UL)
recursionTest 0UL
这样我偶尔会在我的代码中放一个断点,看看x的值很快就会上升,但它仍然没有抱怨。 F#不介意无限递归吗?
答案 0 :(得分:9)
您的recursionTest
函数是尾递归的,这意味着所有递归调用都发生在'尾部位置',即作为函数中的最后一个动作。这意味着F#编译器不需要为递归调用分配新的堆栈帧,因此不会发生堆栈溢出。
尾递归是tail call的一个特例,尾调用本身而不是其他函数。
答案 1 :(得分:4)
通常,F#会发出.NET尊重的尾调用指令:
http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.tailcall(v=vs.95).aspx
在某些特定的简单情况下,与您的一样,F#使用循环将使用递归的程序重写为等效的程序。
答案 2 :(得分:3)
这是tail call optimization的示例,因此编译器将其优化为一个简单的循环。请参阅:http://blogs.msdn.com/b/fsharpteam/archive/2011/07/08/tail-calls-in-fsharp.aspx
尝试这样的事情:
let rec recursionTest x =
recursionTest x + recursionTest (x * 2)