2011-04-03 77 views
4

我一直試圖做的Haskell中2nd Project Euler problem,但我已經得到:Occurs check: cannot construct the infinite type: a = [a]出現,請檢查:無法構造無限類型:A = [A]

fibonacci 0 _ = 0 
fibonacci 1 _ = 1 
fibonacci x xs = (xs!!(x-2)) + (xs!!(x-1)) 

fibonaccisLessThan = takeWhile(<40) $ foldr fibonacci [] [0..] 

sumOfEvenFibonaccis = sum $ filter even $ fibonaccisLessThan 

main = putStrLn $ show $ sumOfEvenFibonaccis 

有人能告訴我爲什麼嗎?

+1

如果你能給錯誤的路線,那將是很好的。作爲一個側面說明,當你有多個'$'時,你通常希望使用合成來優雅。 – alternative 2011-04-03 11:41:14

回答

9

想想第一至第五行。你想實現什麼?你想得到一個懶惰的斐波那契列表。但是你的方法很奇怪。我沒有看到你的代碼,但我想我對你想做的事有一個想法。嘗試給你的函數類型簽名,然後你會看到很快,出了什麼問題。

這裏是一個簡短的形式:

的短切你的方法思考。讓我們試着定義一個斐波那契數列的懶惰列表:

fibs = undefined 

那麼,現在呢?我們知道的第一件事是,前兩個元素是0和1:

fibs = 0 : 1 : undefined 

其餘的呢?這是fibs添加了fibs的移位版本。看這個。 zipWith f結合列表與使用功能f另一個列表:

fibs = 0 : 1 : zipWith (+) fibs (tail fibs) 

而且我們在這裏。就這樣。

+15

您的解決方案可以說是規範的(+1),但我們應該努力幫助OP瞭解編譯器消息。 – Ingo 2011-04-03 12:39:14

8

您的錯誤消息的原因是函數fibonacci返回一個數字,但是,您在第5行使用它,就好像它會返回一個相同類型的數字的列表。

這會導致類型檢查器嘗試將類型「a」與類型「[a]」統一起來,這會發生檢查錯誤。

想一想:你的程序在哪裏是實際構建的列表?你知道,應該有一個:運算符直接或間接應用於某個地方,但事實並非如此。因此,在你的程序中,斐波那契數列表完全是虛構的。

3

讓我們首先注意到,斐波那契數開始具有類型Int -> [Int] -> Int,而foldr相似的類型爲(a -> b -> b) -> b -> [a] -> b,所以我們不能給到斐波納契因爲FOLDR我們不能Int -> [Int] -> Int統一a-> b-> b。這是錯誤信息的來源。

要修復它,我們必須改變斐波納契函數。 fibonacci = 0 : 1 : zipWith (+) fibonacci (tail fibonacci)是做這件事的經典haskell方式,更多方法請看this haskellwiki page。然後,所有你需要做的是:

sumOfEvenLessThen n = sum . filter even . takeWhile (<n) 
main = print $ sumOfEvenLessThen 40 fibonacci 
3

一些一般性的建議:如果你得到一個奇怪的類型錯誤,請確保您有所有頂級定義類型簽名。這確保您的類型錯誤將在正確的定義中報告。這就是說,你的斐波那契定義很奇怪。

1

其他答案已經指出錯誤來自何處,並顯示了在Haskell中非常緊湊和優雅的「標準」斐波那契數字定義。這裏是另一種方式來定義它,這可能是更初學者友好:

fibs = map fst $ iterate next (0,1) where 
    next (x,y) = (y,x+y) 

的想法是保持不只有最後的軌道,但最後斐波那契數,可使用兩人一組做的。使用這個技巧,定義遞歸關係非常容易。

我們從參數(0,1)開始,並使用next函數在此起始值上重新生成一個列表:[(0,1),(1,1),(1,2),(2,3),(3,5)...]。接下來唯一要做的就是「扔掉」對的第二個參數,由map fst $ ...完成。

[更新]

另外一個不錯的定義(工作像zipWith-版本類似)是:

fibs = 0 : scanl (+) 1 fibs