deadfire19
deadfire19

Reputation: 329

Haskell: Non-exhaustive pattern - Checking if list is ascending

I have no idea why my function doesn't work. I have gone through all the posts about non-exhaustive functions but my functions fulfills all possible options as far as I can see.

ascending :: [Int] -> Bool
ascending []                = error "Empty list given"
ascending [x]               = True
ascending [x,y]     | y>=x  = True
                    | x<y   = False
ascending (x:y:xs)  | y>=x  = (ascending (y:xs))
                    | x<y   = False

Result:

*Main> ascending []
*** Exception: Empty list given
*Main> ascending [1]
True
*Main> ascending [1, 2]
True
*Main> ascending [2, 1]
*** Exception: test01.hs:(51,1)-(56,55): Non-exhaustive patterns in function ascending

It works for one pair, but not if the pair is not ascending. When I follow my code it should be just returning False.

Upvotes: 1

Views: 1151

Answers (1)

jub0bs
jub0bs

Reputation: 66234

Have a closer look at the guards for your [x,y] pattern:

ascending [x,y] | y>=x = True
                | x<y  = False

When applied to [2,1], the first guard is checked and evaluates to False (because 2 >= 1); then, the second guard is checked, but it also evaluates to False (because 1 < 2). Therefore, the next pattern is used (because [2,1] also matched (x:y:ys)), but exactly the same thing happens. Because this was the last pattern, GHC rightfully screams at you.

The inequalities in your guards are not complementary. Your third pattern should read

ascending [x,y] | x <= y = True
                | x >  y = False

or, to leave less room for error,

ascending [x,y] | x <= y    = True
                | otherwise = False

However, there is still much room for improvement. In particular:

  • The third pattern overlaps with the fourth one.
  • Because your function returns a Bool, using guards only to explicitly return a Boolean value is redundant.
  • Because, by convention (see dfeuer's comment), the empty list is considered to be ascending, you don't need to throw an error upon encountering it (unless you're following your own whimsical convention).

Taking all this into account, you could have simply written

ascending :: [Int] -> Bool
ascending (x:y:xs) = x <= y && ascending (y:xs)
ascending _        = True

Finally, you can condense the code some more with a combination of and and zipWith:

ascending :: [Int] -> Bool
ascending xs = and $ zipWith (<=) xs (tail xs)

Upvotes: 4

Related Questions