2012-09-06 41 views
6

我寫了下面的代碼創建了斐波那契數的無限名單:可以使用摺疊創建無限列表嗎?

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

,在上面的代碼中使用foldlfoldr避免遞歸寫?

+0

雖然這是有趣的,實際上是一個更好的方式來做到這一點是使用[封閉形式的解決方案,尋找斐波那契數(HTTP ://en.wikipedia.org/wiki/Fibonacci_number#Computation_by_rounding) –

+2

@AndrewWalker,用浮點運算,這不是一個確切的解決方案。 – huon

+0

請注意,在像Haskell這樣的非嚴格函數語言中,出於效率原因沒有理由避免遞歸,但可以出於文體原因避免它。 – AndrewC

回答

11

我不知道是否可以用foldl創建無限列表。你也許可以通過使用foldr來解決這個問題,但是你必須創建另一個列表來摺疊。那個清單會是什麼?斐波那契數字並沒有表明它們是從其他列表中產生的。

你想要的是使用unfoldr。它可以用來創建列表而不是使用它們,例如foldlfoldr。以下是如何使用unfoldr來生成斐波納契數字的無限列表。

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

您可以找到unfoldr在基礎包模塊Data.List英寸

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 

一個簡單的例子,甚至不工作,就會對永遠循環。

14

foldlfoldr功能列表-消費者。正如svenningsson's answer正確地指出,unfoldr是一個列表-生產者它適合於捕獲co-fibs的遞歸結構。

然而,考慮到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) [] 

爲每個輸入產生一點輸出,從無限輸入提供無限輸出。

厚臉皮的人可以因此表達任何infinitary遞歸作爲無限列表上的非遞歸foldr。例如,

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

(編輯:或者,對於這個問題

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

這是在問題更接近的定義)

雖然這種觀察,而真正的,幾乎沒有指示一種健康的編程風格。

+0

感謝您的回答,這非常有趣。但是使用fib再次調用fib使用**遞歸**,我們試圖避免使用fold。 – Dragno

+2

我的術語中的「fib」與lambda綁定,而不是由遞歸定義。另一方面,我確實使用'foldr'來構造一個通用的不動點操作符。 – pigworker

+1

在您的foldl定義中,您省略了** f **的應用程序,所以'foldl fa(b:bs)= foldl(fab)bs'必須是'foldl fa(b:bs)= foldl f(fab)bs' –

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)