Haskell“没有实例”错误

时间:2014-02-21 19:07:13

标签: haskell

我正在尝试编写一个检查数字是否为素数的函数。我写了这个:

primeCheck :: Int -> Int -> Bool
primeCheck n i 
    | n == 2 = True
    | i == 1 = True
    | n `mod` i == 0 = False
    | otherwise = primeCheck n (i -1)

isPrime :: Int -> Bool
isPrime n = primeCheck n (floor (sqrt n))

我得到了这些错误:

  

没有(RealFrac Int)的实例         使用floor' Possible fix: add an instance declaration for (RealFrac Int) In the second argument of primeCheck',即(floor (sqrt n))' In the expression: primeCheck n (floor (sqrt n)) In an equation for isPrime':           isPrime n = primeCheck n(floor(sqrt n))

No instance for (Floating Int)
  arising from a use of `sqrt'
Possible fix: add an instance declaration for (Floating Int)
In the first argument of `floor', namely `(sqrt n)'
In the second argument of `primeCheck', namely `(floor (sqrt n))'
In the expression: primeCheck n (floor (sqrt n)) Failed, modules loaded: none.

当我将代码更改为此以希望解决问题时:

primeCheck :: Int -> Int -> Bool
primeCheck n i 
    | n == 2 = True
    | i == 1 = True
    | n `mod` i == 0 = False
    | otherwise = primeCheck n (i -1)

isPrime :: Int -> Bool
isPrime n = primeCheck n (floor (RealFrac (sqrt  (Floating n))))

我明白了:

  

不在范围内:数据构造函数`RealFrac'

     

不在范围内:数据构造函数“浮动”

我该如何解决这个问题?

3 个答案:

答案 0 :(得分:8)

Floating类型类,而不是构造函数或函数。您似乎已经发现需要转换n的类型。正确的方法是使用fromIntegral

isPrime n = primeCheck n $ floor $ sqrt $ (fromIntegral n :: Double)

我们可以通过遵循函数的类型签名来了解其原因。

isPrime的类型签名中,我们看到n的类型为Int

由于sqrt需要一些Floating类型(即类型为Floating的实例的类型),我们可以将Int转换为{{1}使用Double。请注意fromIntegral的签名是

fromIntegral

(Integral a, Num b) => a -> b Int的实例(所以输入类型没问题),IntegralDouble的实例,所以输出类型没问题。

然后我们使用Num获取新的sqrt

Double期望一个参数,其类型是floor的实例。 RealFrac恰好是Double Floating的实例,因此它可以完成工作(无需转换)。 RealFrac会将平方根转换回floor类型。

请注意,由于Int的输出类型是多态的,因此fromIntegralsqrt的输入类型也是如此,我们必须将转换类型指定为{{1}否则编译器将不知道要转换为哪个 floor / Double / Num实例。您可能会看到错误Floating

您可以使用Hoogle

查看许多功能的类型签名

修改

事实证明RealFrac的显式类型签名是而不是。因此

ambiguous type 'a' in ...

就足够了。在我看来,提供明确的签名会更清楚,但在这种情况下并不是必需的。您可以阅读更多相关信息here

答案 1 :(得分:2)

问题在于从Int转换以便在sqrt中使用。将最后一行更改为

isPrime n = primeCheck n (floor (sqrt (fromIntegral n)))

答案 2 :(得分:2)

FloatingRealFrac是类型类,而不是函数。

此页面概述了标准的Haskell数字类型层次结构(第6.3节):

http://www.haskell.org/onlinereport/basic.html

特别是,这个类型类图非常有用:

http://www.haskell.org/onlinereport/classes.gif

您显然希望使用浮点Intsqrt函数来取floor的平方根。问题是这些函数具有以下类型:

sqrt :: Floating a => a -> a
floor :: (Integral b, RealFrac a) => a -> b

障碍是:

  1. sqrt收到Floating个参数,而Int不在Floating类型
  2. floor需要RealFrac参数,但sqrt只生成Floating
  3. 您可以使用fromIntegral函数解决第一个问题:

    fromIntegral :: (Integral a, Num b) => a -> b
    

    允许您将Int(或任何其他整数类型)转换为任何Num类型(包括Floating。)

    接下来,查看数字类型层次结构的图表,我们看到DoubleFloatFloatingRealFrac类型类的成员。

    因此,通过对Double的明确类型强制,您可以应用floor

    isqrt :: Int -> Int
    isqrt n = floor (sqrt (fromIntegral n) :: Double)
    

    现在,正如@lthread所说,你可以逃脱:

    isqrt :: Int -> Int
    isqrt n = (floor . sqrt . fromIntegral) n
    

    并且GHC会将sqrt的返回类型默认为Double。这样做完全没问题,但你应该知道它正在发生。例如,如果要使用此方法获取Integer的整数平方根,则需要通过一个精确的实数类型(如Data.Number.BigFloat)而不是{{1} }}