我编写了以下代码,它创建了一个无限的Fibonacci数列表:
fibs = 1:1:fib 1 1
where fib a b = a+b:fib b (a+b)
可以使用foldl
或foldr
编写上述代码以避免递归吗?
答案 0 :(得分:14)
foldl
和foldr
功能是list- 使用者。正如svenningsson's answer正确指出的那样,unfoldr
是list- 生成器,适合捕获fibs
的 co </ em> - 递归结构
但是,鉴于foldl
和foldr
的返回类型是多态的,即他们通过使用列表生成的内容,可以合理地询问它们是否可能被用于使用一个列表并产生另一个。这些产生的列表中的任何一个都可能是无限的吗?
查看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
。它可以用于创建列表而不是消费它们,就像foldl
和foldr
一样。以下是如何使用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
将无法正常工作,它将永远循环。