简体   繁体   中英

Haskell basic function definition problem

I'm learning Haskell and I'm trying to write a function to return a list of factors for a number. Here's what I have:

factors :: Int -> [Int]
factors n = [x | x <- [2..s], n `mod` x == 0]
    where s = floor (sqrt n)

When I try to load the module in ghci , I get two errors,

p003.hs:3:14:
    No instance for (RealFrac Int)
      arising from a use of `floor' at p003.hs:3:14-27
    Possible fix: add an instance declaration for (RealFrac Int)
    In the expression: floor (sqrt n)
    In the definition of `s': s = floor (sqrt n)
    In the definition of `factors':
        factors n = [x | x <- [2 .. s], n `mod` x == 0]
                  where
                      s = floor (sqrt n)

p003.hs:3:21:
    No instance for (Floating Int)
      arising from a use of `sqrt' at p003.hs:3:21-26
    Possible fix: add an instance declaration for (Floating Int)
    In the first argument of `floor', namely `(sqrt n)'
    In the expression: floor (sqrt n)
    In the definition of `s': s = floor (sqrt n)
Failed, modules loaded: none.

Any suggestions?

The parameter has type Int , so you cannot calculate a square root for it. You need to convert it to a floating point type first, which you can do with fromIntegral. Unlike some other languages, Haskell does not automatically promote integers to floating point numbers (nor do any other automatic type conversions).

So change sqrt n to sqrt (fromIntegral n) .

The cause of the problem

The type of the sqrt function is

sqrt :: (Floating a) => a -> a

You can check this by typing :t sqrt in ghci.

Int is not an instance of Floating , which is why you're seeing the second error.

The cause of the first error is the same; checking :t floor reveals that the type is:

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

The function is expecting an instance of RealFrac , and you're supplying an Int .

Typing :info RealFrac or :info Floating reveals that neither has an instance for Int , which is why the body of the error says

No instance for ... Int


The solution

The solution to this problem, is to make sure that the types are correct; they must be members of the proper type classes.

A simple way to do this is to use the fromIntegral function, which :t reveals is of type:

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

Using fromIntegral is necessary because the incoming type is Int , but the functions floor and sqrt operate on types RealFrac and Floating , respectively.

It's allowed because, as you can see from the type signature, fromIntegral returns an instance of Num , which includes both the RealFrac and Floating types. You can convince yourself of this by typing :info Num and :info Float into ghci, and viewing the output.

Making his change to your program would have the final result below, which should work as you want:

factors :: Int -> [Int]
factors n = [x | x <- [2..s], n `mod` x == 0] 
    where s = floor (sqrt $ fromIntegral n) 

Further reading

Two good resources for understanding exactly what's going on are the Haskell tutorial's sections on Type Classes and Numbers .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM