0
votes

I'm getting the error "No instance for (Fractional Int) arising from a use of ‘/’", from inside a local definition in a function I'm trying to make which determines how many of the (three) given integers are above the average of all of the given integers. So I've created a local definition inside the function to calculate the average so I can then use it for guard checks. I've used the same code in a separate definition (in another file) to calculate the average and it works. I've tried putting the "fromIntegral" function call in different places but it's not working, where am I going wrong?

Here's the code:

howManyAboveAverage :: Int -> Int -> Int -> Int
howManyAboveAverage a b c
    | a > average && b > average = 2
    | a > average && c > average = 2
    | b > average && c > average = 2
    | a > average = 1
    | b > average = 1
    | c > average = 1
    | otherwise = 0
                where
                average = fromIntegral (a + b + c) / 3

The error is being flagged up on the last line.

Thanks.

4

4 Answers

1
votes

You're comparing a (which is an Int) and average (which is Fractional a => a). Since (>) :: a -> a Bool, GHC presumes that average is also an Int, which doesn't work. You either need to change average to Int (for example via round) or a, b and c to Double or Float.

1
votes

You can also compare integers instead. The idea is that x < (a + b + c)/3 is equivalent to 3*x < a + b + c.

howManyAboveAverage :: Int -> Int -> Int -> Int
howManyAboveAverage a b c
    | gti a av && gti b av = 2
    | gti a av && gti c av = 2
    | gti b av && gti c av = 2
    | gti a av = 1
    | gti b av = 1
    | gti c av = 1
    | otherwise = 0
        where 
            av = a + b + c
            gti x m = 3*x > m
1
votes

Here's a cleaner version of the solution you came up with:

howManyAboveAverage :: Int -> Int -> Int -> Int
howManyAboveAverage a b c
    | a' > average && b' > average = 2
    | a' > average && c' > average = 2
    | b' > average && c' > average = 2
    | a' > average = 1
    | b' > average = 1
    | c' > average = 1
    | otherwise = 0
  where
    average = a' + b' + c' / 3
    a' = fromIntegral a
    b' = fromIntegral b
    c' = fromIntegral c

You can simplify this even further to:

howManyAboveAverage a b c = length (filter (> average) [a', b', c'])
  where
    average = a' + b' + c' / 3
    a' = fromIntegral a
    b' = fromIntegral b
    c' = fromIntegral c
0
votes

I've managed to resolve this by adding "fromIntegral" in front of any parameter that was compared to the average.

New (working) code:

howManyAboveAverage :: Int -> Int -> Int -> Int
howManyAboveAverage a b c
    | fromIntegral a > average && fromIntegral b > average = 2
    | fromIntegral a > average && fromIntegral c > average = 2
    | fromIntegral b > average && fromIntegral c > average = 2
    | fromIntegral a > average = 1
    | fromIntegral b > average = 1
    | fromIntegral c > average = 1
    | otherwise = 0
                where
                average = fromIntegral (a + b + c) / 3

It looks a bit messy / cumbersome so if anyone has any other (cleaner) suggestions please let me know.