2010-12-11 119 views
2

所以,我正在学习Haskell的过程中,并且经常会遇到type/type-classes相关的错误。一些非常明显的愚蠢的错误,有些让我觉得哈斯克尔不适合我。不管怎么说,我有这样的一段代码...比较积分值和浮点值

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)部分出了问题,因为它是有什么关系唯一的部分210。如果我将其更改为p < n一切正常,我得到正确的答案。但我真的想检查平方根,所以我该怎么做?

而且顺便说一句,这不是功课,如果感觉,这是我在上projecteuler.net解决问题第47

感谢任何帮助的尝试。

而且,请不要给我解决上述项目欧拉问题,我想尽我所能做到这一点:)。谢谢。

回答

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

+0

waw,所以这么少。我尝试使用'floor $ sqrt',但我没有把'fromIntegral',需要习惯这个东西我猜:)。是的,我必须把类型签名也:) – 2010-12-11 05:37:30

5

我想提供一些额外的说明,即使已经提供了解决方案。

特别是,类型类别是不是类型。您不能拥有积分值或浮动值。那些不是类型,它们是类型类。这不像面向对象的分类。

Integral a => a -> a -> a这样的签名并不意味着“一个函数需要两个积分参数,并返回一些积分值”。它的意思是“一个函数接受两个类型为a的值,并返回一个类型为a的值。另外,输入a必须是一个Integral的实例。”两者之间的差异是显着的。记住这两个参数和返回值是相同的类型非常重要。 “a”不能在类型签名中改变其含义。

那么这是什么意思?

那么,首先,没有一个先验要求,即一个类型不能成为Integral和Floating的实例。如果您有这种类型,则不需要转换。然而,每个类型类的语义含义使得单一类型很难以有意义的方式成为两者的一个实例。

其次,你应该对如何谈论类型类更加小心。它们与面向对象的子类型根本不同。由于概念上的差异,也有术语差异。精确地使用术语有助于理解差异,并与他人交流。

那么我怎么会说这个问题呢?例如“我如何将整数转换为浮点和返回的实例?我需要使用sqrt :: Floating a => a -> a函数,并将其结果与Integer值进行比较。”

+0

啊,非常感谢卡尔,我确实有关于类型和类型类的概念上的区别(我正在阅读“学习Haskell为好”一书),但当我说“浮动值”时,我的意思是“一个类型为”Floating'类的实例“的值。仍然习惯于术语:) – 2010-12-11 10:53:22

0

你已经得到了你的答案,但也许这有助于避免在功能类似的问题:

http://www.haskell.org/haskellwiki/Converting_numbers

正如有人其背景几乎完全是在动态类型语言,有时我有同样的问题,所以我习惯这个习惯开始写签名的时候写一个Haskell函数。它确实帮助我推理我期望的和我出错的地方。

+0

谢谢,我想我应该停止像对待动态语言一样对待Haskell,并开始添加类型签名:) – 2010-12-11 10:55:10

+0

您当然不需要,Haskell的类型推断非常好。我只是发现它帮助我思考我的代码。 – 2010-12-11 12:03:20