Search code examples
haskellruntime-errornon-exhaustive-patterns

Non-exhaustive patterns in function Exception in haskell


I have this code:

import Data.Char
foo  :: String -> String
foo (x:xs) = if (digitToInt(x)) == 0 
                     then foo xs
                     else if (digitToInt(x)) /= 0   
                          then replicate (digitToInt(x)) (head $ take 1 xs) ++ foo (tail xs )
                     else ""    

input: foo "4a5b"

output: "aaaabbbbb"

this line if (digitToInt(x)) == 0 checks if the number is 0 if so then it will expand with the rest of the string:

Example:

input: foo "00004a5b"

output: "aaaabbbbb"

and I added else if (digitToInt(x)) /= 0 to check for the other cases.

However it gives me:

*Main> foo "4a5b"
"aaaabbbbb*** Exception: test.hs:(89,1)-(93,28): Non-exhaustive patterns in function foo

error. Which cases did I miss here?


Solution

  • Non-exhaustive patterns in function foo

    This error occurs at runtime because the function has not been declared according to the pattern given in the evaluation. This particular case does not consider the end of the tail of a list.

    The solution that I present below is an intention to generalize the use case presented.

    Note the definition of the function reproduce.

    reproduce []        .. --Empty list
    reproduce (x:[])    .. --A single value in the list
    reproduce (x:y:[])  .. --A list with only two elements x and y
    reproduce (x:y:xs)  .. --A list with the first two elements x and y and the rest of the list xs
    

    Here is a solution to the problem presented considering a generalization of the foo function.

    The code:

    --We import the functions to use
    import Data.List (replicate, group, groupBy, all)
    import Data.Char (isDigit)
    
    --Grouping by digits
    gbDigit :: String -> [String]
    gbDigit = groupBy (\x y -> (isDigit x) && (isDigit y)) --ETA reduce form
    
    --Grouping by nor digits
    gbNoDigit :: String -> [String]
    gbNoDigit s = fmap concat $ groupBy (\x y -> not (all isDigit x) && not (all isDigit y)) (gbDigit s)
    
    --Prepare applying grouping 
    --by digit first and not by digit afterwards, therefore:
    prepare = gbNoDigit
    
    foo :: String -> String
    foo x = concat $ reproduce (prepare x)
    
    reproduce :: [String] -> [String]
    reproduce [] = []        --Empty list, nothing to do
    reproduce (x:[]) = []    --A numerical value and nothing to replicate, nothing to do
    reproduce (x:y:[]) = (replicate (read x::Int)) y --A numeric value and a string to replicate, so we replicate
    reproduce (x:y:xs) = (replicate (read x::Int)) y <> reproduce xs --A numeric value, a string to replicate, and a tail to continue
    

    Step by step:

    • Given an entry "003aA3b4vX10z"
    • Group by Digits: ["003","a","A","3","b","4","v","X","10","z"]
    • Group by NoDigits: ["003","aA","3","b","4","vX","10","z"]
    • Reproduce & Concat: "aAaAaAbbbvXvXvXvXzzzzzzzzzz"

    Test cases:

    Prelude> foo "00004a5b"
    Prelude> "aaaabbbbb"
    
    Prelude> foo "4a"
    Prelude> "aaaa"
    
    Prelude> foo "4a10B"
    Prelude> "aaaaBBBBBBBBBB"
    
    Prelude> foo "3w1.1w6o1l4y1.1com"
    Prelude> "www.woooooolyyyy.com"