可以折叠用于创建无限列表吗?

时间:2012-09-06 10:35:39

标签: haskell

我编写了以下代码,它创建了一个无限的Fibonacci数列表:

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

可以使用foldlfoldr编写上述代码以避免递归吗?

4 个答案:

答案 0 :(得分:14)

foldlfoldr功能是list- 使用者。正如svenningsson's answer正确指出的那样,unfoldr是list- 生成器,适合捕获fibs co <​​/ em> - 递归结构

但是,鉴于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

更接近问题中的定义。)

虽然这种观察虽然是真实的,但并不能说明健康的编程风格。

答案 1 :(得分:11)

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

您想要的是使用unfoldr。它可以用于创建列表而不是消费它们,就像foldlfoldr一样。以下是如何使用unfoldr生成无限的斐波纳契数列表。

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

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

答案 2 :(得分:2)

避免显式递归的一种方法是使用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)

答案 3 :(得分:1)

您可以使用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

将无法正常工作,它将永远循环。