是方检吗?

时间:2010-12-02 09:30:08

标签: haskell

我正在尝试编写函数来检查参数是否为整数的平方:

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
一切都很好。

为什么我会收到错误以及如何解决?

3 个答案:

答案 0 :(得分:10)

由于类型不匹配,您收到错误。 sqrt的类型为sqrt :: Floating a => a -> atruncate的类型为truncate :: (RealFrac a, Integral b) => a -> b。前者表示sqrt将任何浮点数作为输入,并返回与输出相同的类型之一;后者说它可以将任何实数小数 1 截断为任何整数。但是,您断言xInt,而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;无论如何它都是默认的,但很清楚(尽管没有必要)。 DoubleFloatingRealFrac的实例,因此您可以sqrttruncate。我还重新安排了你的代码;它是如何编写的,因为这样我们只计算truncationsqrt一次。另请注意,如果删除类型签名,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 => t9.0或任何带小数点的数字,其类型为Fractional t => t)。这意味着它们可以是任何类型的数字,这是一件好事。否则,9必须只是IntInteger,并定义新的数字类型 - 甚至同时使用IntInteger 2 - 会成为皇室的痛苦。因此,当您撰写truncate(sqrt(9))时,GHCi会确定9必须是Floating(来自sqrt)和RealFrac(来自truncate的实例),默认为Double,使一切正常。这种违约是数字类型的标准行为(这就是为什么你可以在:: Double的定义中省略isSquare),尽管不是为了其他任何东西(except in GHCi,为了方便而扩展它) 。由于9不仅仅是Int,而是x,因此您无需转换9,但您需要转换x


1: FloatingRealFrac之间的区别在于,Complex DoubleFloating的实例,但不是{{} 1}},RealFracRational但不是RealFrac的实例。 FloatingFloat是两者的实例。

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..]