【问题标题】:foldr not returning with an infinite listfoldr 没有返回无限列表
【发布时间】:2015-01-09 02:12:40
【问题描述】:

我已经阅读了https://www.haskell.org/haskellwiki/Foldl_as_foldr 和其他一些关于 foldl 和 foldr 之间区别的博客文章。现在我正在尝试将斐波那契数列写成一个带有 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 时,函数不会返回。我认为 foldr 是体递归的,允许您在这些类型的情况下将它与无限列表一起使用。为什么这不适用于我的解决方案?

【问题讨论】:

  • 您似乎一直在 buildFibs 中添加到列表的头部。您的列表是无限的,但方向错误=P

标签: haskell recursion lazy-evaluation


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    这是一个很好的方程式推理练习:

    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]
    

    【讨论】:

      猜你喜欢
      • 2021-12-14
      • 2023-04-09
      • 2012-10-08
      • 2015-08-21
      • 2016-02-01
      • 2016-01-06
      • 1970-01-01
      • 1970-01-01
      • 2020-05-15
      相关资源
      最近更新 更多