我试图理解 Haskell 中的尾递归。我想我明白它是什么以及它是如何工作的,但我想确保我没有把事情搞砸。
这是“标准”阶乘定义:
factorial 1 = 1
factorial k = k * factorial (k-1)
例如,在运行时factorial 3
,我的函数将调用自身 3 次(给予或接受)。如果我想计算阶乘 99999999,这可能会造成问题,因为我可能会出现堆栈溢出。在我到达之后,factorial 1 = 1
我将不得不在堆栈中“返回”并乘以所有值,所以我有 6 个操作(3 个用于调用函数本身,3 个用于乘以值)。
现在我向您介绍另一种可能的阶乘实现:
factorial 1 c = c
factorial k c = factorial (k-1) (c*k)
这也是递归的。它会调用自己 3 次。但它不存在仍然必须“返回”来计算所有结果的乘法的问题,因为我已经将结果作为函数的参数传递了。
据我所知,这就是尾递归的含义。现在,它似乎比第一个好一点,但您仍然可以轻松地发生堆栈溢出。我听说 Haskell 的编译器会在后台将 Tail-Recursive 函数转换为 for 循环。我想这就是为什么做尾递归函数有回报的原因?
如果这就是原因,那么如果编译器不打算做这个聪明的把戏,那么绝对没有必要尝试使函数尾递归——我是对的吗?例如,虽然理论上 C# 编译器可以检测尾递归函数并将其转换为循环,但我知道(至少我听说过)目前它还没有这样做。因此,如今使函数尾递归绝对没有意义。是这样吗?
谢谢!