I'm trying to create a function which finds the pair of a character inputted, in a list, that is also inputted.
findc :: Char -> [(Char, Char)] -> Char
findc x list = head[ if null c then x else c| (n,c) <- list, x == n ]
if no letter is found in list that matches input, the function is meant to return the value inputted. The above code doesn't work as null is invalid type for c.
the following code only works for when a letter is found:
findc :: Char -> [(Char, Char)] -> Char
findc x list = head[ c| (n,c) <- list, if x == n then c==c else c==x]
> findc 'B' [('A','F'), ('B','G'), ('C','H')]
'G'
but when not found it doesn't work, as it returns empty list rather than the inputted char.
How can i return the inputted char if char not found in list?
CodePudding user response:
There is already a function in Prelude that does most of what you want:
lookup :: Eq a => a -> [(a, b)] -> Maybe b
With this, all you have to do is call lookup
and then distinguish the Just
and Nothing
cases:
findc :: Eq a => a -> [(a, a)] -> a
findc goal table = case lookup goal table of
Nothing -> goal
Just subst -> subst
But of course there is also a function for unwrapping a Maybe with a default value:
fromMaybe :: a -> Maybe a -> a
Combining these two, we get a simple definition:
findc :: Eq a => a -> [(a, a)] -> a
findc goal table = fromMaybe goal (lookup goal table)
If you are so inclined, you can play some code golf here, by noting that the table
argument is just sorta handed from findc
into its implementation at the end, which you can make implicit:
findc :: Eq a => a -> [(a, a)] -> a
findc goal = fromMaybe goal . lookup goal
Finally, the truly maniacal might try to eliminate the goal
argument, which is an interesting exercise, but does not yield a very readable result:
findc :: Eq a => a -> [(a, a)] -> a
findc = liftA2 (.) fromMaybe lookup
CodePudding user response:
Your first code contains the right kind of ideas but in a wrong order. You can put the if expression outside of the comprehension:
findc :: Char -> [(Char, Char)] -> Char
findc x list =
let filtered = [ c | (n,c) <- list, x == n ]
in if null filtered then x else head filtered
But it is more idiomatic to use pattern matching instead of an if expression and null
and head
:
findc :: Char -> [(Char, Char)] -> Char
findc x list =
case [ c | (n,c) <- list, x == n ] of
[] -> x
(y:_) -> y
CodePudding user response:
Rather than using a list comprehension, you could use Data.List.find
. This function returns a Maybe
type, which you can pattern match on to either return the character provided, or the corresponding character from the list of tuples.
import Data.List
findc :: Char -> [(Char, Char)] -> Char
findc ch lst =
let match = find ((ch ==) . fst) lst
in
case match of
Nothing -> ch
Just (_, v) -> v
CodePudding user response:
This is another way of doing this:
data Result a = Found a | NotFound a deriving Show
lst = [('A','F'), ('B','G'), ('C','H')]
findc :: Char -> [(Char, Char)] -> Result Char
findc x [] = NotFound x
findc x (n:ns)
| x == fst n = Found (snd n)
| otherwise = findc x ns
main = do
print $ findc 'B' lst -- Found 'G'
print $ findc 'D' lst -- NotFound 'D'
I created a new data structure for the result, either Found value
or NotFound value
. This prevents the problem where we have a list including ('B','B'), which returns the search term even though the search term has successfully matched.