折叠左尾递归?

时间:2017-06-14 07:08:51

标签: haskell recursion

我想问一下,函数foldl是否是尾递归?

当我查看来源时,foldl是如何实现的:

foldl :: (b -> a -> b) -> b -> [a] -> b
foldl f acc [] = acc
foldl f acc (x:xs) = foldl f (f acc x) xs

它看起来像尾递归。

左侧折叠始终保持关联状态。假设我有以下表达式:

foldl (*) 1 [1..3]

然后它将评估为:

((1 * 1) * 2) * 3

然后写出步骤,我会这样做:

foldl (*) 1 [1..3] =
foldl (*) (1 * 1) [2,3]
foldl (*) (1 * 2) [3]
foldl (*) (2 * 3) []
6

不喜欢((1 * 1) * 2) * 3,或者我错了?

1 个答案:

答案 0 :(得分:2)

foldl严格时,

f表示尾递归。你是对的,

更好
foldl (*) 1 [1,2,3] = 
foldl (*) 1 [2,3]   = 
foldl (*) 2 [3]     = 
foldl (*) 6 []      = 
6 

如果f不严格,即保留其参数表达式,稍后计算,最终会得到像(((1*1)*2)*3)这样的嵌套表达式,这甚至会导致堆栈溢出,不必要地,当它们最终被评估时,因为要评估这样的嵌套表达式,必须使用堆栈:

(((1*1)*2)*3) 
=> x * 3 where x = ((1*1)*2)
            => x = y*2 where y = 1*1
                             y <= 1
            => x = 1*2
            <= x = 2
=> 2 * 3
<= 6

并且无论如何,当中间结果已经完全评估时,这是浪费很多努力。

另见:Does Haskell have tail-recursive optimization?

必须始终使用

编辑: foldl'as noted在评论中user:chi