我正在尝试编写一个检查数字是否为素数的函数。我写了这个:
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'
不在范围内:数据构造函数“浮动”
我该如何解决这个问题?
答案 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
的实例(所以输入类型没问题),Integral
是Double
的实例,所以输出类型没问题。
然后我们使用Num
获取新的sqrt
。
Double
期望一个参数,其类型是floor
的实例。 RealFrac
恰好是Double
和 Floating
的实例,因此它可以完成工作(无需转换)。
RealFrac
会将平方根转换回floor
类型。
请注意,由于Int
的输出类型是多态的,因此fromIntegral
和sqrt
的输入类型也是如此,我们必须将转换类型指定为{{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)
Floating
和RealFrac
是类型类,而不是函数。
此页面概述了标准的Haskell数字类型层次结构(第6.3节):
http://www.haskell.org/onlinereport/basic.html
特别是,这个类型类图非常有用:
http://www.haskell.org/onlinereport/classes.gif
您显然希望使用浮点Int
和sqrt
函数来取floor
的平方根。问题是这些函数具有以下类型:
sqrt :: Floating a => a -> a
floor :: (Integral b, RealFrac a) => a -> b
障碍是:
sqrt
收到Floating
个参数,而Int
不在Floating
类型floor
需要RealFrac
参数,但sqrt
只生成Floating
您可以使用fromIntegral
函数解决第一个问题:
fromIntegral :: (Integral a, Num b) => a -> b
允许您将Int
(或任何其他整数类型)转换为任何Num
类型(包括Floating
。)
接下来,查看数字类型层次结构的图表,我们看到Double
和Float
是Floating
和RealFrac
类型类的成员。
因此,通过对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} }}