foldr没有以无限列表返回

时间:2015-01-09 02:12:40

标签: haskell recursion lazy-evaluation

我已阅读https://www.haskell.org/haskellwiki/Foldl_as_foldr以及其他一些关于foldl和foldr之间差异的博客文章。现在我正在尝试将斐波那契序列作为带有折叠器的无限列表编写,我想出了以下解决方案:

fibs2 :: [Integer]
fibs2 = foldr buildFibs [] [1..]
  where
    buildFibs :: Integer -> [Integer] -> [Integer]
    buildFibs _ [] = [0]
    buildFibs _ [0] = [1,0]
    buildFibs _ l@(x:s:z) = (x + s):l

但是当我执行take 3 fibs2时,该函数不会返回。我认为折叠是身体递归允许你在这些类型的情况下使用无限列表。为什么这不适用于我的解决方案?

2 个答案:

答案 0 :(得分:7)

问问自己:哪个斐波那契数字将是列表中的第一个?我对你的代码的解读是这个问题的答案是,#34;最大的一个" (从理论上讲,buildFibs的每次迭代都会在结果列表的头部添加一个稍大的数字)。由于有无限多的斐波那契数,这需要一段时间来计算!

答案 1 :(得分:4)

这对于等式推理是一个很好的练习:

fibs2 = foldr buildFibs [] [1..]

foldr f z [] = z
foldr f z (x:xs) = f x (foldr f z xs)

foldr buildFibs [] [1..] =
    buildFibs 1 (foldr buildFibs [] [2..]) =
    buildFibs 1 (buildFibs 2 (foldr buildFibs [] [3..])) =
    buildFibs 1 (buildFibs 2 (buildFibs 3 (foldr buildFibs [] [4..]))) =
    ...

我希望到现在你可以看到问题:foldr试图在返回之前遍历整个列表。如果我们使用foldl代替什么呢?

foldl f z [] = z
foldl f z (x:xs) = foldl f (f z x) xs

buildFibs' = flip buildFibs

foldl buildFibs' [] [1..] =
    foldl buildFibs' (buildFibs 1 []) [2..] = 
    foldl buildFibs' [0] [2..] =
    foldl buildFibs' (buildFibs 2 [0]) [3..] =
    foldl buildFibs' [0,1] [3..] =
    foldl buildFibs' (buildFibs 3 [0,1]) [4..] =
    foldl buildFibs' (0+1 : [0,1]) [4..] =
    foldl buildFibs' [1,0,1] [4..] =
    foldl buildFibs' (buildFibs 4 [1,0,1]) [5..] =
    foldl buildFibs' (1+0 : [1,0,1]) [5..] =
    foldl buildFibs' [1,1,0,1] [5..] =
    foldl buildFibs' (buildFibs 5 [1,1,0,1]) [6..] =
    foldl buildFibs' [2,1,1,0,1] [6..] =
    -- For brevity I'll speed up the substitution
    foldl buildFibs' [3,2,1,1,0,1] [7..] =
    foldl buildFibs' [5,3,2,1,1,0,1] [8..] =
    foldl buildFibs' [8,5,3,2,1,1,0,1] [9..] =
    ...

因为你可以看到你可以使用buildFibsfoldl来实际计算斐波纳契数,但遗憾的是你正在构建一个向后的无限列表,你永远无法计算出列表中的特定元素,因为foldl永远不会终止。您可以计算有限数量的它们:

> take 10 $ foldl buildFibs' [] [1..10]
[34,21,13,8,5,3,2,1,1,0]