我有以下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 t1@(_, _, x) t2@(_, _, 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
类型呢?或者是否有其他原因会导致此错误?
答案 0 :(得分:16)
Haskell通常可以将0
等数字文字的类型推断为您需要的任何类型。这是因为它知道你传递给他们的功能;如果我有一个函数phi :: Integer -> Integer
,并且我调用phi 0
,则Haskell知道该特定0
必须是Integer
。如果我用pho :: Int -> Int
调用函数pho 0
,也可以。 特定0
被推断为Int
。
但是Int
和Integer
是不同的类型,并且一个特定的0
无法传递给phi
和pho
。
您的问题很简单,maxRatio
处理的元组是由({你)(Int, Int, Double)
键入的,但是这样的元组构造为(n, phi n, ratio)
。由于phi
接受并返回Integer
,因此该表达式中的n
必须为Integer
。但那对maxRatio
不起作用,所以你得到了错误。
根据您实际需要的类型(Int
或Integer
),您需要做的就是更改phi
或maxRatio
的类型签名,以便他们'重新使用相同的号码。 Haskell将决定你的字面编写的0
是什么数字类型是必要的,提供有一个可以使它工作!
请注意,错误消息具体告诉您,n
中(n, phi n, ratio)
的{{1}}预计为Int
,实际上是{{1} }}。永远不会提到Integer
元组。通常,类型错误源于编译器指向您的位置以外的其他位置(因为编译器可以做的就是发现不同的推理链对某些类型的类型产生不一致的要求,无法知道整个过程的哪个部分是“错误的” ),但在这种情况下它做得很好。
Haskell得到一个(相当合理的)糟糕的代表,因为不可思议的错误消息,但它可以从编译器告诉你的问题开始,并尝试找出为什么事实它抱怨你的代码产生了。这一开始会很痛苦,但是你会很快在Haskell的错误消息(至少是更直接的错误消息)中开发一个基本知识,这将有助于你真正快速地发现这些类型的错误,这使编译器成为一个非常强大的错误检测系统为你。
答案 1 :(得分:4)
n
的类型, Int
被推断为maxRatio
,而phi
的类型应该是Integer
。最简单的解决方法是将maxRatio
的类型更改为使用Integer
,甚至只更改a
,因为它不会触及这些值。
答案 2 :(得分:3)
正在推断,因此您只需更改maxRatio
的类型签名即可。不过,如果您需要将明确的Int
更改为Integer
,请使用toInteger :: (Integral a) => a -> Integer
答案 3 :(得分:1)
您的类型签名不一致 - 始终将Int
替换为Integer
。