3

所以,我正在学习 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 个问题的尝试

谢谢你的帮助。

而且,请不要给我解决上述项目欧拉问题的方法,我想尽可能自己做:)。谢谢。

4

3 回答 3

6

你的问题是......好吧......你不能比较积分和浮点值:-)你必须明确指出整数和浮点数之间的转换。该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将我们的浮点值转换回整数(请注意,这会向下取整!)。

其次,我建议养成在顶层声明中添加类型签名的习惯。它不仅能让你更好地掌握语言,而且如果你不这样做,你可能会违反单态性限制

于 2010-12-11T05:16:02.643 回答
5

即使已经提供了解决方案,我也想对正在发生的事情提供一些额外的说明。

特别是,类型类不是类型。您不能有 Integral 值或 Floating 值。这些不是类型,它们是类型类。这不像面向对象的子类型。

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

那么,这意味着什么?

嗯,首先,没有先验要求类型不能同时是 Integral 和 Floating 的实例。如果您有这样的类型,则无需进行任何转换。然而,这些类型类中的每一个的语义使得单一类型很难以有意义的方式成为两者的实例。

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

那么我该如何表达这个问题呢?诸如“如何将 Integer 转换为 Floating 实例并返回?我需要使用该sqrt :: Floating a => a -> a函数,并将其结果与 Integer 值进行比较。”

于 2010-12-11T08:50:52.943 回答
0

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

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

作为一个几乎完全是动态类型语言背景的人,我有时会遇到同样的问题,所以我养成了在编写 Haskell 函数时总是从签名开始的习惯。它确实有助于我推理我的期望以及我哪里出错了。

于 2010-12-11T10:20:01.800 回答