batlord
batlord

Reputation: 13

Haskell error: non-exhaustive patterns

I'm trying to write a function that will count the words in a sentence.

cwords :: String -> Int
cwords "" = 0
cwords (a:b:c)
    |isAlpha a && (isAlpha b == False) = 1 + cwords (b:c) 
    |otherwise = cwords (b:c)

I keep getting an error saying "non-exhaustive patterns in function cwords" whenever I enter a sentence. An empty string works fine. (I'm very new to Haskell)

Upvotes: 1

Views: 96

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476614

The problem is that you define two clauses:

  • one with the empty list

    cwords "" = 0
    
  • and one where the list contains at least two elements:

    cwords (a:b:c) = ...
    

So the Haskell says that it does not know what to do in case the string contains exactly one character, since there is no clause that specifies what to do in that case.

Since you count words, in case we obtain the last character, we should count this as a word (given it is an alpha-character). So the code should read:

cwords :: String -> Int
cwords "" = 0
cwords (a:b:c)
    |isAlpha a && (isAlpha b == False) = 1 + cwords (b:c) 
    |otherwise = cwords (b:c)

to:

cwords :: String -> Int
cwords "" = 0
cwords [_] | isAlpha a = 1
cwords (a:b:c)
    |isAlpha a && (isAlpha b == False) = 1 + cwords (b:c)
    | otherwise = cwords (b:c)

That being said, we can still improve the code:

  • we can use not (isAlpha b) instead of isAlpha b == False;
  • if we know b is not isAlpha, then we do not have to perform recursion on (b:c) here, but can directly perform recursion on c; and
  • we can rewrite the otherwise case as a clause that handles lists with at least one element.

Resulting into:

cwords :: String -> Int
cwords "" = 0
cwords [a] | isAlpha a = 1
cwords (a:b:c) |isAlpha a && not (isAlpha b) = 1 + cwords c
cwords (_:b) = cwords b

Upvotes: 5

Related Questions