Search code examples
haskellfunctional-programming

How can I write a function that counts a number of elements in the list that are strictly greater and strictly lower than the given number?


I'm trying to learn Haskell through this course, and I'm a bit stuck with the last assignment in the first module. The problem statement sounds as follows:

Write a function that takes a number and a list of numbers and returns a string, saying how many elements of the list are strictly greater than the given number and strictly lower.

lowerAndGreater 3 [1 .. 9]
"3 is greater than 2 elements and lower than 6 elements"

Explanation: the list [1 .. 9] contains 9 elements: [1, 2, 3, 4, 5, 6, 7, 8, 9] The given number 3 is greater than 2 elements (1 and 2) and lower than 6 elements (4, 5, 6, 7, 8 and 9).

🕯 HINT: Use recursion to implement this function.

Here is my attempt to solve the problem:

lowerAndGreater :: Int -> [Int] -> String
lowerAndGreater n list = show(n) ++ " is greater than " ++ show(lesserThanN n list) ++ " elements and lower than " ++ show(greaterThanN n list) ++ " elements"
    where
        greaterThanN :: Int -> [Int] -> Int
        greaterThanN greater l =
            if null l
            then greater
            else if head l > n
                 then greaterThanN (greater + 1) (tail l)
                 else greaterThanN greater       (tail l)

        lesserThanN :: Int -> [Int] -> Int
        lesserThanN lesser l =
            if null l
            then lesser
            else if head l < n
                 then lesserThanN (lesser + 1) (tail l)
                 else lesserThanN lesser       (tail l)

Unfortunately, the execution results aren't what I expect, e.g.

lowerAndGreater 3 [1 .. 9]
"3 is greater than 5 elements and lower than 9 elements"

Could you please advice where my mistake is?


Solution

  • It might be better to enumerate over the list a single time. We can do this with a function that returns a 2-tuple with:

    import Control.Arrow (first, second)
    
    lowerAndGreater :: Ord a => a -> [a] -> (Int, Int)
    lowerAndGreater y = foldr go (0, 0)
      where
        go x
            | x < y = first (+ 1)
            | x > y = second (+ 1)
            | otherwise = id

    This then gives us:

    ghci> lowerAndGreater 3 [1 .. 9]
    (2,6)