haskellrecursionlist-comprehensionhaskell-platform

Remove elements from input Haskell


I want to make the function remove :: String -> Char -> String which removes all instances of the character in the second argument from the string in the first argument. For example:

remove "ab cd ef" ' ' == "abcdef"

remove "ab cd cf" 'c' == "ab d f"

What I have so far is:

remove (x:xs) y 
     | x == y     = xs
     | otherwise  = x : remove xs y

This only removes one element, e.g. if I write remove "ab cd ef" ' ' it prints out "abcd ef" and not "abcdef".

I need to make one function using recursion and another one using list comprehension.


Solution

  • As chi pointed out in the comments, you need to use recursion in both branches or you'll end up only removing the first instance.

    For completeness here is the correct code:

    remove [] _ = []
    remove (x:xs) y
         | x == y     = remove xs y
         | otherwise  = x : remove xs y
    

    If you do not include the [] case then you will get an error about pattern matching -- this is because when you match the pattern (x:xs) you are assuming that the array has length of >= 1 for x to be defined. If you think of lists like data List a = Cons a (List a) | Empty, then any functions that take a list must pattern match over the Cons and the Empty constructors. Obviously, if you have an empty string there's nothing to remove so [] maps to [] for all y.

    If you want list comprehension as well, recognise that list comprehension is map and filter combined. We don't want to change the characters which map allows us to do, only to filter out the ones which match y.

    The syntax [f x | x <- xs, predicate x] means "take every x from xs where the expression predicate x is True, and return a list of expressions f x. The f x is the map part, the predicate x is the filter part.

    Thus we can write:

    remove xs y = [x | x <- xs, x /= y]
    

    Note here that we don't pattern match the list value constructors, so we don't need two cases. The x <- xs part simply means we get [] if the input list is empty. We've also used x or id x instead of f x, and our predicate is x /= y.

    Also FYI the template-haskell tag isn't necessary here, that's a metaprogramming language extension.