【问题标题】:Comparing an Integral and a Floating values比较积分值和浮点值
【发布时间】:2010-12-11 05:04:19
【问题描述】:

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

感谢您的帮助。

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

【问题讨论】:

    标签: haskell prime-factoring


    【解决方案1】:

    您的问题是...嗯...您无法比较 Integral 和 Floating 值 :-) 您必须明确指出整数和浮点数之间的转换。 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

    【讨论】:

    • 哇,这么少,这么多。我尝试使用floor $ sqrt,但我没有放fromIntegral,我猜需要习惯这些东西:)。是的,我也必须输入类型签名:)
    【解决方案2】:

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

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

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

    那么这一切意味着什么?

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

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

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

    【讨论】:

    • 啊,非常感谢 Carl,我确实对类型和类型类有概念上的区别(我正在阅读“Learn you a Haskell for great good”一书),但是当我说“@987654323 @value”,我的意思是“一个类型为 Floating 类型类实例的值”。仍然习惯术语:)
    【解决方案3】:

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

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

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

    【讨论】:

    • 谢谢,我想我应该停止将 Haskell 视为动态语言并开始添加类型签名:)
    • 你当然不必,Haskell 的类型推断非常好。我刚刚发现它有助于我思考我的代码。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多