I have to write a shift function that searches an element's pair from a "table" (list). And if the element is not in the given list, it has to give a '#' az a result.
Examples:
shift [('b', 'g'), ('c', 'h'), ('a', 'f')] 'a' == 'f'
shift [('b', 'g'), ('c', 'h'), ('a', 'f')] 'b' == 'g'
shift [('b', 'g'), ('c', 'h'), ('a', 'f')] 'b' == 'g'
shift [('b', 'g'), ('c', 'h'), ('a', 'f')] 'x' == '#'
My code:
shift :: [(Char,Char)] -> Char -> Char
shift z c = [b |(a,b)<-z,a==c]!!0
It works, but not for the exceptions. I can't seem to make it work for elemnts that are not in the list. I have tried:
shift z c
| c `elem` z =[b |(a,b)<-z,a==c]!!0
| otherwise ='#'
and a helper function:
isgood z c
| c `elem` z = (shift z c)
| otherwise = '#'
But they don't work. How to solve this?
There are lots of ways you can go about writing this, which all essentially do the same thing.
One way is just a slight modification of one of your previous attempts, the one which "works, but not for the exceptions":
shift :: [(Char,Char)] -> Char -> Char
shift z c = [b |(a,b)<-z,a==c]!!0
The only problem with this, as I'm sure you've observed, is that the program crashes when you failed to find the character, rather than the function giving you '#' as desired. This is because (!! 0)
, like head
(which it is equivalent to), fails with an ugly runtime error when applied to an empty list. So all you need to do is to check if the list is empty or not, and either take the first element or give the default answer. The neatest way to do this in my opinion is to pattern match in a case
expression:
shift :: [(Char,Char)] -> Char -> Char
shift z c = case [b |(a,b)<-z,a==c] of
[] -> '#'
(c:_) -> c
But this concept of looking something up in a "lookup table" in the form of a list of pairs is so standard that the Haskell Prelude already has a function for it, called, naturally, lookup. It returns a Maybe
type as a safe way of handling failure, and you can pattern match on the result in a similar way as in the previous version if you want:
shift z c = case (lookup c z) of
Nothing -> '#'
Just c -> c
and this can be further simplified using fromMaybe, another standard library function:
shift z c = fromMaybe '#' (lookup c z)
Two further points to finish:
shift
is a strange name for this function, since it needs a lookup table of arbitrary pairs. For a genuine Caesar shift cipher the table is implicit in the shift number, and you would want a function of type Int -> Char -> Char
, which would be implemented rather differently.