所以,我正在学习Haskell,并经常陷入类型/类型相关的错误。一些非常明显的愚蠢错误,以及一些让我觉得haskell的错误对我来说不合适。无论如何,我有这段代码......
pfactors' ps n
| p > (sqrt n) = []
| m == 0 = p : (pfactors' ps q)
| otherwise = pfactors' (tail ps) n where
p = head ps
(q, m) = divMod n p
pfactors = pfactors' primes
main = print $ pfactors 14
(有些背景:pfactors函数应该取一个数字并返回一个素数列表,它是给定数字的主要因子。primes
是素数的无限列表)
这给了我这个错误:
p47.hs:10:11:
Ambiguous type variable `a' in the constraints:
`Floating a' arising from a use of `pfactors'' at p47.hs:10:11-26
`Integral a' arising from a use of `primes' at p47.hs:10:21-26
Possible cause: the monomorphism restriction applied to the following:
pfactors :: a -> [a] (bound at p47.hs:10:0)
Probable fix: give these definition(s) an explicit type signature
or use -XNoMonomorphismRestriction
现在我明白这是p < (sqrt n)
部分的问题,因为它是唯一与Floating
有关的部分。如果我将其更改为p < n
一切正常,我会得到正确的答案。但我真的想用平方根来检查,那我该怎么做呢?
顺便说一句,这不是作业,如果感觉就好,那是我在projecteuler.net上解决第47个问题的尝试
感谢您的帮助。
而且,请不要给我解决上述项目的euler问题,我想尽可能多地自己做:)。感谢。
答案 0 :(得分:5)
你的问题是......嗯......你无法比较积分和浮点值:-)你必须明确指出整数和浮点数之间的转换。 sqrt :: (Floating a) => a -> a
函数适用于浮点数,但是您主要处理整数,因此您无法免费使用它。尝试这样的事情:
pfactors' ps n
| p > (floor $ sqrt $ fromIntegral n) = []
| m == 0 = p : (pfactors' ps q)
| otherwise = pfactors' (tail ps) n where
p = head ps
(q, m) = divMod n p
在这里,我们使用fromIntegral :: (Integral a, Num b) => a -> b
将整数转换为其他内容,允许我们将其用作sqrt
的参数。然后,我们说floor
将我们的浮点值转换回整数(注意这会向下舍入!)。
其次,我建议养成在顶级声明中添加类型签名的习惯。它不仅可以让你更好地掌握语言,而且如果你不这样做,你可能会对monomorphism restriction犯规。
答案 1 :(得分:5)
我想提供一些额外的说明,即使已经提供了解决方案。
特别是,类型类是 NOT 类型。您不能拥有积分值或浮动值。那些不是类型,它们是类型类。这与面向对象的子类型不同。
像Integral a => a -> a -> a
这样的签名并不意味着“一个带有两个积分参数的函数,并返回一些积分值。”它表示“一个函数,它接受两个类型为a的值,并返回一个类型为a的值。另外,键入a必须是Integral的一个实例。”两者之间的差异很大。记住参数和返回值都是相同的类型非常重要。 “a”不能在类型签名中改变其含义。
那么这一切意味着什么?
首先,没有先验要求类型不能是Integral和Floating的实例。如果你有这样的类型,则不需要转换。但是,每个类型类的语义含义使得单个类型很难以有意义的方式成为两者的实例。
其次,在谈论类型类时,你应该更加小心。它们与面向对象的子类型根本不同。由于概念上的差异,也存在术语上的差异。准确掌握您的术语有助于理解差异,并与他人沟通。
那么我该怎么说这个问题呢?像“我如何将整数转换为浮动和返回的实例?我需要使用sqrt :: Floating a => a -> a
函数,并将其结果与整数值进行比较。”
答案 2 :(得分:0)
您已经得到了答案,但这可能有助于避免功能中的类似问题:
http://www.haskell.org/haskellwiki/Converting_numbers
作为背景几乎全部采用动态类型语言的人,我有时会遇到同样的问题,所以我习惯在编写Haskell函数时始终从签名开始。它确实帮助我推理我的期望和我出错的地方。