Is it square check
I am trying to write function to check if the argument is square of integer:
isSquare :: Int -> Bool
isSquare x = truncate(sqrt(x)) * truncate(sqrt(x)) == x
When I loading the function I get the error:
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)) == 开发者_JS百科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.
But if i try to execute:
Prelude> truncate(sqrt(9))*truncate(sqrt(9))==9
True
all is fine.
Why I get the error and how to fix it ?
You're getting the errors because of type mismatches. The type of sqrt
is sqrt :: Floating a => a -> a
, and the type of truncate
is truncate :: (RealFrac a, Integral b) => a -> b
. The former says that sqrt
takes as input any floating-point number, and returns one of the same type as output; the latter says it can truncate any real fractional number1 into any integral number. However, you assert that x
is an Int
, and an Int
isn't a floating-point number. Thus, the second error: "No instance for (Floating Int)
arising from a use of `sqrt
'". This says that because of sqrt x
, it wanted Int
to be a floating-point number, but there's no definition for that. Your first error is similar: since sqrt :: Floating a => a -> a
, its output is the same as its input, so you're trying to call truncate
on an integer. This of course makes no sense, since Int
is not a RealFrac
, and that's why you get the first error. Fixing this is easy:
isSquare :: Int -> Bool
isSquare x = let x' = truncate $ sqrt (fromIntegral x :: Double) in x'*x' == x
The fromIntegral
function has the type fromIntegral :: (Integral a, Num b) => a -> b
; it can convert any integral number into any number at all. This is why we need to tell Haskell that we want it to produce a Double
; it'd default to that anyway, but it's nice to be clear (though not necessary). Double
is an instance both of Floating
and RealFrac
, so you can sqrt
and truncate
it. I also rearranged your code a little; the way it is up there is how I'd write it, since this way we only compute the truncation
and sqrt
once. Also, note that if you remove the type signature, Haskell will infer the more general type isSquare :: Integral a => a -> Bool
, since you never assume that x
is precisely an Int
.
The reason that truncate(sqrt(9))*truncate(sqrt(9))==9
successfully returned True
is because of the type of 9
. You can ask GHCi to tell you this:
Prelude> :t 9
9 :: (Num t) => t
In Haskell, all integral numeric literals have the type Num t => t
(9.0
, or any number with a decimal point, has the type Fractional t => t
). This means that they can be any kind of number at all, which is a good thing. Otherwise, 9
would have to just be an Int
or Integer
, and defining new number types—or even using both Int
and Integer
!2—would be a royal pain. Thus, when you write truncate(sqrt(9))
, GHCi determines that 9
must be an instance of Floating
(from sqrt
) and RealFrac
(from truncate
), which it defaults to Double
, making everything work. This defaulting is standard behavior for numeric types (it's why you could leave out the :: Double
in my definition of isSquare
), though not for anything else (except in GHCi, which extends it for convenience). Since 9
isn't just an Int
, but x
is, you don't need to convert 9
, but you do need to convert x
.
1: The difference between Floating
and RealFrac
is that, for instance, Complex Double
is an instance of Floating
but not RealFrac
, and Rational
is an instance of RealFrac
but not Floating
. Float
and Double
are instances of both.
2: In case you haven't come across this, the difference is that Int
is finite-precision, and Integer
is arbitrary-precision.
You're treating integers as floats. Hence, the types don't match.
Use fromIntegral
:
isSquare :: Int -> Bool
isSquare n = truncate(sqrt(x)) * truncate(sqrt(x)) == n
where x = fromIntegral n
Not all that efficient but a cute way of determining if a number is a square, using integer arithmetic only:
isSquare x = x == head (dropWhile (< x) squares)
where squares = scanl1 (+) [1,3..]
精彩评论