2012-09-05 56 views
8

我有以下Haskell代碼:無法比擬預期的類型'詮釋「與實際類型'整數」

-- Problem 69 

import ProjectEuler 

phi :: Integer -> Integer 
phi n = n * product [p - 1 | p <- primeDivisors n] `div` product [p | p <- primeDivisors n] 
-- primeDivisors n is a list of the prime divisors of n 

maxRatio :: (Int, Int, Double) -> (Int, Int, Double) -> (Int, Int, Double) 
maxRatio [email protected](_, _, x) [email protected](_, _, y) 
    | x > y = t1 
    | otherwise = t2 

main = print (foldl 
       maxRatio 
       (0, 0, 0.0) 
       [(n, phi n, ratio) | n <- [2..max], let ratio = fromIntegral n/(fromIntegral (phi n))] 
      ) 
    where max = 1000 

它提供了以下錯誤:

Couldn't match expected type `Int' with actual type `Integer' 
In the expression: n 
In the expression: (n, phi n, ratio) 
In the third argument of `foldl', namely 
    `[(n, phi n, ratio) | 
     n <- [2 .. max], 
     let ratio = fromIntegral n/(fromIntegral (phi n))]' 

我懷疑的是,在三聯(0, 0, 0.0) 0的類型爲Int。是0總是鍵入Int或ghci推斷類型爲Int在這種情況下?如果以後,我該如何強制它改爲輸入Integer?還是有其他的東西導致這個錯誤?

回答

15

哈斯克爾一般可以推斷數值文字的類型,如0的任何適當的類型,你需要他們。這是因爲它知道你傳遞給他們的功能;如果我有一個函數phi :: Integer -> Integer,並且我呼叫phi 0,Haskell知道那個特定的0肯定是Integer。如果我用pho 0調用函數pho :: Int -> Int也是好的; 特定0被推斷爲Int

然而IntInteger是不同的類型,而且也沒有辦法一個特定0可以傳遞給兩個phipho

你的問題很簡單,maxRatio處理的元組是由你輸入的(Int, Int, Double),但是一個這樣的元組被構造爲(n, phi n, ratio)。由於phi需要並返回Integer,該表達式中的n必須是Integer。但那對maxRatio不起作用,所以你會得到這個錯誤。

根據所鍵入你其實想(IntInteger),所有你需要做的是改變,使他們與同種數的工作phimaxRatio類型簽名。哈斯克爾會決定你的字面書寫0是什麼數字類型是必要的,使之提供提供有一個可以使它工作!

注意,錯誤傳遞消息專門告訴你,這是(n, phi n, ratio)n這有望成爲一個Int竟是一個Integer。從來沒有提到過這個元組。通常,類型錯誤起源於編譯器所指向的地方以外的地方(因爲編譯器可以做的就是發現不同的推理鏈對某種類型的東西產生不一致的要求,而無法知道整個過程的哪一部分是「錯誤的」 ),但在這種情況下,它做得很好。

哈斯克爾得到了不可思議的錯誤信息(相當有道理)壞名聲,但它可以有很大的幫助,從什麼編譯器告訴你的問題開始,揣摩爲什麼它在抱怨的事實出現從你的代碼。這一開始會很痛苦,但是你很快就會在Haskell的錯誤信息(至少更直接的錯誤信息)中發展出基本的讀寫能力,這將幫助你很快發現這些錯誤,這使得編譯器成爲一個非常強大的錯誤檢測系統爲你。

+0

感謝您的明確解釋。我認爲'0'是它的類型的多態,但是我的noob眼睛看不到任何其他類型推理的原因,導致了錯誤。當然,我只是錯過了我的'maxRatio'的顯式類型聲明。 –

4

n被推斷爲Int由於maxRatio類型,而phi類型說它應該是Integer。最簡單的解決方法是將maxRatio的類型更改爲使用Integer,或者甚至只更改a,因爲它不會觸及這些值。

+0

Doh!我錯過了那個細節。我正在重溫一些我後來編寫的代碼,可能在我學會幾乎完全使用Integer之前。 –

3

這是推測,所以你可以改變maxRatio的型號簽名。不過,如果你需要一個明確的Int更改爲Integer,使用toInteger :: (Integral a) => a -> Integer

+0

我通常使用'fromIntegeral',特別是當我使用'length'時。使用'fromIntegral'而不是'toInteger'有什麼區別? –

+0

@ Code-Guru:我認爲唯一的區別是'fromIntegral'可以返回任何'Num'類型,所以在閱讀它時將會推斷爲什麼類型。 – amindfv

+1

@ Code-Guru:實際上,'fromIntegral'被定義爲'fromIntegral = fromInteger。 toInteger' - 所以它絕對是靈活性和可讀性之間的折中 – amindfv

1

您的類型簽名不一致 - 全部替換IntInteger

相關問題