每个Haskell函数都会进行尾调用吗?

时间:2015-03-30 08:04:57

标签: haskell recursion operators call tail-call

我想知道Haskell中的每个函数都应该是尾递归的。

作为非尾递归函数实现的阶乘函数:

fact 0 = 1
fact n = n * fact (n - 1)

每个操作符也是一个函数,所以这相当于

fact 0 = 1
fact n = (*) n (fact (n - 1))

但这显然是对我的尾巴。我想知道为什么这个代码会导致堆栈溢出,如果每次调用只是在堆上创建一个新的thunk。难道我不会有堆溢出吗?

1 个答案:

答案 0 :(得分:2)

在代码中

fact 0 = 1
fact n = (*) n (fact (n - 1))
正如您所观察到的那样,

最后(*) ...是尾调用。然而,最后一个参数fact (n-1)将构建一个由(*)立即要求的thunk。这导致对fact的非尾调用。递归地,这将消耗堆栈。

TL; DR:发布的代码执行尾调用,但(*)没有。

(Haskell中的“堆栈”在严格的语言中也是一个不那么清晰的概念.Haskell的一些实现使用比这更复杂的东西。如果你想要的话,可以搜索“push / enter vs eval / apply”策略一些血腥的细节。)