我正在尝试编写函数来检查参数是否为整数的平方:
isSquare :: Int -> Bool
isSquare x = truncate(sqrt(x)) * truncate(sqrt(x)) == x
当我加载函数时,我收到错误:
Prelude> :load "some.hs"
[1 of 1] Compiling Main ( some.hs, interpreted )
some.hs:2:13:
No instance for (RealFrac Int)
arising from a use of `truncate' at some.hs:2:13-29
Possible fix: add an instance declaration for (RealFrac Int)
In the first argument of `(*)', namely `truncate (sqrt (x))'
In the first argument of `(==)', namely
`truncate (sqrt (x)) * truncate (sqrt (x))'
In the expression: truncate (sqrt (x)) * truncate (sqrt (x)) == x
some.hs:2:22:
No instance for (Floating Int)
arising from a use of `sqrt' at some.hs:2:22-28
Possible fix: add an instance declaration for (Floating Int)
In the first argument of `truncate', namely `(sqrt (x))'
In the first argument of `(*)', namely `truncate (sqrt (x))'
In the first argument of `(==)', namely
`truncate (sqrt (x)) * truncate (sqrt (x))'
Failed, modules loaded: none.
但如果我尝试执行:
Prelude> truncate(sqrt(9))*truncate(sqrt(9))==9
True
一切都很好。
为什么我会收到错误以及如何解决?
答案 0 :(得分:10)
由于类型不匹配,您收到错误。 sqrt
的类型为sqrt :: Floating a => a -> a
,truncate
的类型为truncate :: (RealFrac a, Integral b) => a -> b
。前者表示sqrt
将任何浮点数作为输入,并返回与输出相同的类型之一;后者说它可以将任何实数小数 1 截断为任何整数。但是,您断言x
是Int
,而Int
不是浮点数。因此,第二个错误:“使用”(Floating Int)
“”而产生的sqrt
没有实例。这说明因为sqrt x
,它希望Int
成为一个浮点数,但是没有定义。您的第一个错误类似:自sqrt :: Floating a => a -> a
以来,其输出与其输入相同,因此您尝试在整数上调用truncate
。这当然没有意义,因为Int
不是RealFrac
,这就是你得到第一个错误的原因。解决这个问题很容易:
isSquare :: Int -> Bool
isSquare x = let x' = truncate $ sqrt (fromIntegral x :: Double) in x'*x' == x
fromIntegral
函数的类型为fromIntegral :: (Integral a, Num b) => a -> b
;它可以将任何整数转换为任何数字。这就是为什么我们需要告诉Haskell我们希望它产生Double
;无论如何它都是默认的,但很清楚(尽管没有必要)。 Double
是Floating
和RealFrac
的实例,因此您可以sqrt
和truncate
。我还重新安排了你的代码;它是如何编写的,因为这样我们只计算truncation
和sqrt
一次。另请注意,如果删除类型签名,Haskell将推断出更一般的类型isSquare :: Integral a => a -> Bool
,因为您从不认为x
恰好是Int
。
truncate(sqrt(9))*truncate(sqrt(9))==9
成功返回True
的原因是9
的类型。您可以要求GHCi告诉您:
Prelude> :t 9
9 :: (Num t) => t
在Haskell中,所有整数数字文字都具有类型Num t => t
(9.0
或任何带小数点的数字,其类型为Fractional t => t
)。这意味着它们可以是任何类型的数字,这是一件好事。否则,9
必须只是Int
或Integer
,并定义新的数字类型 - 甚至同时使用Int
和Integer
! 2 - 会成为皇室的痛苦。因此,当您撰写truncate(sqrt(9))
时,GHCi会确定9
必须是Floating
(来自sqrt
)和RealFrac
(来自truncate
的实例),默认为Double
,使一切正常。这种违约是数字类型的标准行为(这就是为什么你可以在:: Double
的定义中省略isSquare
),尽管不是为了其他任何东西(except in GHCi,为了方便而扩展它) 。由于9
不仅仅是Int
,而是x
,因此您无需转换9
,但您需要转换x
。
1: Floating
和RealFrac
之间的区别在于,Complex Double
是Floating
的实例,但不是{{} 1}},RealFrac
是Rational
但不是RealFrac
的实例。 Floating
和Float
是两者的实例。
2:如果你没有遇到过这种情况,不同之处在于Double
是有限精度的,Int
是任意精度的。
答案 1 :(得分:3)
您将整数视为浮点数。因此,类型不匹配。
使用fromIntegral
:
isSquare :: Int -> Bool
isSquare n = truncate(sqrt(x)) * truncate(sqrt(x)) == n
where x = fromIntegral n
答案 2 :(得分:2)
不是那么有效,而是一种确定数字是否为正方形的可爱方法,仅使用整数运算:
isSquare x = x == head (dropWhile (< x) squares)
where squares = scanl1 (+) [1,3..]