.net 为什么';t无限递归在F#中遇到堆栈溢出异常?
我知道这在某种程度上与人们在询问堆栈溢出问题时遇到的问题相反,但如果我创建一个函数并按如下方式调用它,我从未收到任何错误,应用程序只是研磨我的CPU核心,直到我强制退出它:.net 为什么';t无限递归在F#中遇到堆栈溢出异常?,.net,f#,functional-programming,.net,F#,Functional Programming,我知道这在某种程度上与人们在询问堆栈溢出问题时遇到的问题相反,但如果我创建一个函数并按如下方式调用它,我从未收到任何错误,应用程序只是研磨我的CPU核心,直到我强制退出它: let rec recursionTest x = recursionTest x recursionTest 1 当然,我可以把它改成这样: let rec recursionTest (x: uint64) = recursionTest (x + 1UL) recursionTest 0UL l
let rec recursionTest x =
recursionTest x
recursionTest 1
当然,我可以把它改成这样:
let rec recursionTest (x: uint64) =
recursionTest (x + 1UL)
recursionTest 0UL
let rec recursionTest x =
recursionTest x + recursionTest (x * 2)
这样,我可以偶尔在代码中设置一个断点,看到x的值上升得相当快,但它仍然没有抱怨。F#不介意无限递归吗?这是一个示例,因此编译器正在将其优化为一个简单循环。见此:
试着这样做:
let rec recursionTest (x: uint64) =
recursionTest (x + 1UL)
recursionTest 0UL
let rec recursionTest x =
recursionTest x + recursionTest (x * 2)
您的
recursionTest
函数是尾部递归的,这意味着所有递归调用都发生在“尾部位置”,即作为函数中的最后一个操作。这意味着F#编译器不需要为递归调用分配新的堆栈帧,因此不会发生堆栈溢出
尾部递归是的一种特殊情况,尾部调用是对自身而不是对其他函数的调用。通常,F#发出.NET遵循的tailcall指令:
在某些特定的简单情况下,如您的情况,F#使用循环将使用递归的程序重写为等效程序。谢谢,这也解释了一个未提及的问题,即为什么VS没有在调用堆栈窗口中显示更多调用。事实上,我相信在这种情况下,F#编译器实际上会将代码转换为循环-编译后的代码中实际上没有递归。如果我花额外的时间将应用程序放入IL反汇编程序,可能会看到这一点或者反射器。谢谢你提供额外的背景信息。