2010-06-13 53 views
8

作爲編程啓蒙之旅的一部分(從程序到OOP到併發到現在的功能),我只是在Haskell的世界中蘸了我的腳趾。我一直在嘗試在線Haskell Evaluator學習Haskell地圖,摺疊,循環和遞歸

不過我現在被困在了一個問題:

創建一個簡單的功能,讓數字數組的總和。

在程序語言這對我來說是很容易的(使用遞歸)(C#):

private int sum(ArrayList x, int i) 
{ 
    if (!(x.Count < i + 1)) { 
     int t = 0; 

     t = x.Item(i); 
     t = sum(x, i + 1) + t; 
     return t; 
    } 
} 

在Haskell的一切都非常精細但是我失敗的嘗試是這樣的:

let sum x = x+sum in map sum [1..10] 

這導致以下錯誤(來自上述網站):

Occurs check: cannot construct the infinite type: a = a -> t 

請記住,我在過去的30分鐘內只使用了Haskell!

我不是在尋找答案,而是更多地解釋它。

回答

16

我不只是尋找一個答案,但一更多的解釋。

在=的左側,您使用sum作爲應用於x的函數。編譯器不知道x的類型,所以編譯器使用類型變量a來表示「x的類型」。因此,編譯器不知道函數sum的結果類型,因此它會選擇另一個類型變量(此類型爲t)來表示結果類型。編譯器認爲x的類型爲a -> t(函數接受a並返回t)。

在=的右側添加xsum。在Haskell中,可以添加所有類型的數字,但只有具有類型的類型時,纔可以添加兩個數字。因此編譯器假定sumx類型相同,即類型a

但是在Haskell中,一個標識符有一個類型—也許是一個whangdilly複雜的類型,但是一種類型。這包括sum,其類型應該在`標誌兩側是相同的,所以編譯器試圖求解方程

a = a -> t 

有用於at是解決這個公式沒有值。它根本無法完成。沒有a這樣a等於一個函數,它接受自己作爲參數。因此出現錯誤信息

cannot construct the infinite type: a = a -> t 

即使所有的解釋,這不是一個很好的錯誤信息,是嗎?

歡迎哈斯克爾:-)


附:你可能喜歡嘗試「氦氣,用於學習Haskell」,這爲初學者提供了更好的錯誤信息。

+0

這是我正在尋找的全面解釋。閱讀完這本書並進一步閱讀後,我現在明白了一點。 – Darknight 2010-06-15 21:07:26

12

'sum'將取值列表並將其減少爲單個值。你可以把它寫成一個顯式循環(記住Haskell沒有循環關鍵字,但使用遞歸)。注意如何定義有兩個部分,根據該列表的形狀:

mysum []  = 0 
mysum (x:xs) = x + mysum xs 

或者更有效,在tail-recursive風格:

mysum xs = go 0 xs 
    where 
     go n []  = n 
     go n (x:xs) = go (n+x) xs 

然而,Haskell有控制結構的豐富庫在惰性列表上運行。在這種情況下,減少的一個列表到單個值可以用一個reduce函數完成:摺疊。

所以mysum可以寫爲:

mysum xs = foldr (+) 0 xs 

例如:

Prelude> foldr (+) 0 [1..10] 
55 

你的錯誤是在同一時間使用地圖,其轉換的列表,一個元素,而比

我建議你首先介紹Haskell,或許是「Programming in Haskell」,以感受函數式編程的核心概念。其他好的入門教材都有描述in this question.

+0

我不知道什麼0戲劇的意義,你可以擴大使用零點嗎? – Darknight 2010-06-15 21:10:01

+0

0是摺疊中積累參數的初始值。這是原始來源中的't'值。 – 2010-06-15 21:19:53

1

你需要閱讀一個好的教程,這裏有一些很大的誤解。我會假設你是指列表而不是數組。數組存在於Haskell中,但它們不是初學者會遇到的。 (更不用說你正在使用[1..10],它是數字1到10的列表)。

你想實際上是內置的,並呼籲總和,所以我們只好去其他地方調用我們的一些功能,new_sum:

new_sum [] = 0 
new_sum (h:t) = h + (sum t) 
+0

您的模式匹配語法不正確。 – 2010-06-13 21:45:52

+0

謝謝,沒有嘗試代碼並檢查我的類型(壞我)。固定。 – 2010-06-13 22:12:19

+0

這是(理解)部分,我很難理解,但諾曼的答案和進一步閱讀後,現在是有道理的。 – Darknight 2010-06-15 21:08:55

0

讓我們看看這第一部分:

let sum x = x+sum 

將總和的類型是什麼在這種情況下?它需要一個數字並返回一個函數,該函數返回一個函數,該函數返回一個需要數字的函數,如果您已經寫入數字,則返回函數+ x(x)= 。 和 let sum = + 會返回一個帶兩個整數並添加它們的函數。

所以現在讓我們看看第二部分。 in map sum [1..10] map採用一個參數的函數並將其應用於列表的每個元素。在那裏沒有空間來填充累加器,所以我們來看看其他列表函數,特別是foldl,foldr。這兩個參數都有一個列表和一個初始值的兩個參數的函數。 foldl和foldr之間的區別在於它們開始的一面。升被留所以1 + 2 + 3等和r爲右10 + 9 + 8等

設總和與foldl總和=(+)0 [1..10]