2012-12-19 34 views
4

我開始與Haskell一起工作,但是我試圖做的這種相等性檢查沒有解決。Haskell - 「在應用程序中鍵入錯誤」:「統一會給無限類型」

我有一個函數,countLetter a [b] c,其中a是一個char,b是字符的列表,c是一個int。 (該類型聲明通過精細過去了。)但是我碰上這種表達的問題:

if a == head(b) 

給我下面的消息:

Type error in application 

*** Expression  : a == head b 
*** Term   : a 
*** Type   : [a] 
*** Does not match : a 
*** Because  : unification would give infinite type 

我全部的代碼,如果需要這種耐心,是這樣的:

countLetter :: char -> [char] -> int 

countLetter a [b] c = if null b 

         then [] 
         else 
         if a == head(b) 
         then countLetter a tail(b) c+1 
        else 
        countLetter head(b) tail(b) c 

任何幫助或建議將不勝感激。謝謝。

回答

9

首先,Haskell中的類型以大寫字母開頭。如果您在類型簽名中使用以小寫字母開頭的標識符,則它們將被解釋爲類型變量。因此,您的類型char -> [char] -> inta -> [a] -> b相同,這對您的功能來說不是一種明智的類型。你想要Char -> [Char] -> Int

其次[]不是Int類型的有效值。修復你的類型簽名應該會產生一個錯誤信息,以較不明確的方式告訴你這一點。

應該然後還告訴你,null b是一種錯誤,因爲bChar型的(你的函數的第二個參數是[Char]類型和你匹配它與模式[b],結合b到單Char包含在該列表中)並且null將列表而非Char作爲其參數。

要澄清的最後一個點位:

的第二個參數的函數是一個列表。通過在參數列表中書寫[b],您可以將該參數與模式[b]相匹配。換句話說寫countLetter a [b] c = blabla是一樣的文字:

countLetter a theList c = 
    case theList of 
     [b] -> blabla 

所以你說:「第二個參數的功能必須與一個單一的元素列表和元素將被稱爲b」。這不是你想說的。你想說的是「該函數的第二個參數(順便說一下,它是一個列表)將被稱爲b」。爲此,你只需編寫countLetter a b c = blabla

類型列表的參數不必與其他類型的參數有任何區別。

+0

非常感謝! 我不知道大寫字母,所以這是一個很大的幫助。 正如你所說,我得到了'null b'的錯誤。我曾經以爲b會是一個列表,但我似乎錯了。你能告訴我如何正確表示b作爲列表嗎? 如果我不能使用[],我還不確定如何結束列表。我過去的經歷與Core Haskell有關,雖然我一直在閱讀,但我仍然不知道任何其他方式來結束列表。 –

+0

@SamOfloinn我擴展了我的答案。希望能夠清除一切。 – sepp2k

+0

啊,這很有趣。再次感謝你! 我會嘗試解決我自己得到的下一個問題。祝你晚安! –

2

你會遇到另一個錯誤是這樣的:

countLetter head(b) tail(b) c 

這是由規則,同爲

countLetter head b tail b c 

即在您打電話給countLetter功能有5個參數,當只需要3(根據第一個等式)或2(根據您的類型簽名)。(這是另一點,編譯器會非常不滿。)

你可能想這樣的:

countLetter (head b) (tail b) c 

同樣:

countLetter a tail(b) c+1 

相同

(countLetter a tail b c) + 1 

但你可能想要:

countLetter a (tail b) (c+1) 
1

除了提供修復功能的方法的其他答案之外,您可能需要考慮以更具構圖的風格編寫此代碼。具體來說,尋找可以通過構建其他標準功能來編寫函數的方法。假設你有一個函數可以移除不等於列表中測試信件的所有內容。

myFilter :: Char -> [Char] -> [Char 
myFilter = ... 

使用myFilter後,你會留下只有你檢查元素的列表。在這一點上,你可以使用length得到列表的長度:

countLetter :: Char -> [Char] -> Int 
countLetter a b = length $ myFilter a b 

所以,現在你只需要定義myFilter,可與標準的前奏功能filter

myFilter a b = filter (==a) b 

完成對於函數這個小,創造我們自己的定義是很難值得的,因爲你可以只寫

countLetter a b = length $ filter (== a) b 

現在,在GH定義此CI看到它找到什麼類型的功能countLetter

Prelude> let countLetter a b = length $ filter (== a) b 
Prelude> :t countLetter 
countLetter :: Eq a => a -> [a] -> Int 

看不到Char!實現並不取決於元素是字母,只是可以比較它們是否相等(這也適用於您的方法)。所以ghci在計算類型中反映了這一點。但是,您可以通過用Char代替a來看到這正是您想要的類型。

許多函數式程序員傾向於找到這種方法,即通過編寫較小的部分來構建函數,特別容易推理,所以它可能相當常見。特別是在使用列表時,您可能想要查看是否可以使用所謂的高階函數(例如map,filter或摺疊)編寫實現,而不是使用遞歸。有時遞歸是最明確的方式,但我希望你經常會發現函數組合是一個更好的方法。