【问题标题】:Can fold be used to create infinite lists?fold 可以用来创建无限列表吗?
【发布时间】:2014-07-01 16:18:05
【问题描述】:

我编写了以下代码,它创建了一个无限的斐波那契数列:

fibs = 1:1:fib 1 1
  where fib a b = a+b:fib b (a+b)

上面的代码可以写成foldl或者foldr来避免递归吗?

【问题讨论】:

  • 虽然这很有趣,但实际上还有一个更好的方法是使用closed form solution for finding the Fibonacci numbers
  • @AndrewWalker,使用浮点运算,这不是一个精确的解决方案。
  • 请注意,在像 Haskell 这样的非严格函数式语言中,出于效率原因没有理由避免递归,但出于文体原因避免递归是可以的。

标签: haskell


【解决方案1】:

foldlfoldr 函数是列表-消费者。正如svenningsson's answer 正确指出的那样,unfoldr 是一个列表-生产者,适合捕获fibsco-递归结构。

但是,鉴于foldlfoldr 在它们的返回类型上是多态的,即它们通过使用一个列表来产生什么,因此询问它们是否可以用于使用一个列表并产生另一个列表是合理的。这些生成的列表中的任何一个可能是无限的吗?

foldl的定义

foldl :: (a -> b -> a) -> a -> [b] -> a
foldl f a []        = a
foldl f a (b : bs)  = foldl f (f a b) bs

我们看到foldl 要产生任何东西,它所使用的列表必须是有限的。因此,如果foldl f a 产生无限输出,那是因为a 是无限的,或者是因为f 有时会执行无限列表生成。

foldr 是另一回事

foldr :: (b -> a -> a) -> a -> [b] -> a
foldr f a []        = a
foldr f a (b : bs)  = f b (foldr f a bs)

它承认f 可能会为输入中消耗的每个b 生成一些输出的惰性可能性。像

这样的操作
map g = foldr (\ b gbs -> g b : gbs) []   -- golfers prefer ((:) . g)
stutter = foldr (\ x xxs -> x : x : xxs) []

为每个输入产生一点点输出,从无限输入提供无限输出。

因此,厚脸皮的人可以将任何无限递归表示为无限列表上的非递归foldr。例如,

foldr (\ _ fibs -> 1 : 1 : zipWith (+) fibs (tail fibs)) undefined [1..]

(编辑:或者,就此而言

foldr (\_ fib a b -> a : fib b (a + b)) undefined [1..] 1 1

这更接近问题中的定义。)

虽然这个观察结果是正确的,但很难表明一种健康的编程风格。

【讨论】:

  • 感谢您的回答,这很有趣。但是使用 fib 再次调用 fib 使用 recursion 我们尽量避免使用 fold。
  • fib 在我的术语中与 lambda 绑定,而不是由递归定义。另一方面,我确实使用foldr 来构造一个通用的定点运算符。
  • 在您的 foldl 定义中,您省略了 f 的应用,因此 foldl f a (b : bs) = foldl (f a b) bs 必须是 foldl f a (b : bs) = foldl f (f a b) bs
  • @DavidUnric 谢谢,我的错。现已修复。
【解决方案2】:

我不知道是否可以使用foldl 创建无限列表。您也许可以通过使用foldr 来解决这个问题,但是您必须创建另一个列表来折叠。这份清单会是什么?斐波那契数字没有任何迹象表明它们是从其他列表生成的。

您想要的是使用unfoldr。它可以用来创建列表而不是使用它们,就像foldlfoldr 的情况一样。以下是使用unfoldr 生成无限斐波那契数列的方法。

fib = unfoldr (\(a,b) -> Just (a,(b,a+b))) (1,1)

您可以在基础包中的模块Data.List 中找到unfoldr

【讨论】:

    【解决方案3】:

    避免显式递归的一种方法是使用fix 将递归表示为一个固定点。

    import Data.Function (fix)
    
    fibs = fix $ \l -> [1,1] ++ zipWith (+) l (tail l)
    

    或无点样式

    import Data.Function (fix)
    import Control.Monad.Instances
    
    fibs = fix $ ([1,1] ++) . (zipWith (+) =<< tail)
    

    【讨论】:

      【解决方案4】:

      你可以使用zipWith来写你的定义

      fibonacci = 1:1:zipWith (+) fibonacci (tail fibonacci)
      

      编辑: 好的,我认为您不能使用 foldl 或 foldr 创建无限列表。在任何简单的想象意义上都不是。 如果你看一下 foldl 的简单定义

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

      foldl 在用完整个列表之前永远不会返回。 所以一个简单的例子像

      g = foldl f [] [1..]
       where 
        f xs a = xs ++ [a]
      
      > take 10 g
      

      甚至不会工作,它会永远循环。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-05-16
        • 2020-01-12
        • 2011-09-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多