2012-09-05 43 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

相关问题