How to use non mutually exclusive guards in Haskell?

Viewed 63

Any help with this would be very appreciated.

I am trying to write a function in Haskell to find how many, of three numbers are larger than the average of said three numbers.

The problem is, I am trying to use guards to increment a "sum", but I'm assuming guards only go to whichever the first condition is that's true. Is there a better way to do this?

Here is my code:

howManyAboveAverage :: Int -> Int -> Int -> Int
howManyAboveAverage x y z | aboveAverage x = sum + 1
                          | aboveAverage y = sum + 1
                          | aboveAverage z = sum + 1
                          where
                            aboveAverage a = a > div (x+y+z) 3
                            sum = 0
2 Answers

You can sum up the conditions and convert a Bool to an Int with fromEnum :: Enum a => a -> Bool:

howManyAboveAverage :: Int -> Int -> Int -> Int
howManyAboveAverage x y z = sum (map (fromEnum . (avg <)) [x, y, z])
    where avg = div (x+y+z) 3

or as @luqui says work with length:

howManyAboveAverage :: Int -> Int -> Int -> Int
howManyAboveAverage x y z = length (filter (avg <) [x, y, z])
    where avg = div (x+y+z) 3

Yes, just write it differently, as

howManyAboveAverage :: Int -> Int -> Int -> Int
howManyAboveAverage x y z = aboveAverage x +
                            aboveAverage y +
                            aboveAverage z 
                          where 
                            avg = div (x+y+z) 3
                            aboveAverage a 
                               | a > avg   = 1
                               | otherwise = 0

In expression-oriented programming, we do not set and mutate global variables, we arrange for an expression to have the same ultimate intended value.

Now you can tweak and simplify. In particular, a less hacky way is

howManyAboveAverage :: Int -> Int -> Int -> Int
howManyAboveAverage x y z = length $
                            aboveAverage x ++
                            aboveAverage y ++
                            aboveAverage z
                          where 
                            avg = div (x+y+z) 3
                            aboveAverage a = [a | a > avg]
                            x & f = f x

which translates to a nice list comprehension, as one option.

Or make it more complex and obtuse and less readable if that's your thing.

Related