if i say i have two strings or character lists,
list1 = ["c","a","t"]
list2 = ["d","o","g"]
and if i read a string using Input Output "ct" and pass it to the function,the function should return "dg".
Please give me any idea about such a function.
I would consider taking those two lists, zipping them together, use Data.Map.fromList to create a lookup Map, then map over the input String and use the Map to work out what to replace them with.
I'll first assume list1 and list2 have type [Char] (i.e. String), since that's what your text seems to indicate (your code has them as [String]s -- if you really want this, see the generalized version in the addendum).
If you zip the two lists, you end up with a list of pairs indicating how to translate characters. In your example, zip list1 list2 = [('c','d'), ('a','o'), ('t','g')]. We'll call this our lookup list. Now consider the function lookup:
lookup :: Eq a => a -> [(a, b)] -> Maybe b
In our case, we can specialize this to
lookup :: Char -> [(Char, Char)] -> Maybe Char
so we have something that takes a character and a lookup list and returns a substituted character if the input character is in the lookup list (otherwise a Nothing). Now we just need to glue the things we've found together: We essentially need to map \c -> lookup c lookupList (more elegantly written as flip lookup) over the input string while throwing out any characters not found in the lookup list. Well, enter mapMaybe:
mapMaybe :: (a -> Maybe b) -> [a] -> [b]
It does exactly what we want. Now your function can be written as
replace :: String -> String -> String -> String
replace list1 list2 = mapMaybe ((flip lookup) (zip list1 list2))
You'll need to import Data.Maybe.
Addendum, for when you understand the above: Observe how what we did above had nothing to do with the fact that we were working with lists of characters. We could do everything above with lists of any type for which equality makes sense, i.e. for (lists of) any type which is an instance of the Eq typeclass (cf the signature of lookup above). Moreover, we don't have to translate from that type to itself -- for example, each character above could be sent to say, an integer! So really, we can write
replace :: (Eq a) => [a] -> [b] -> [a] -> [b]
replace list1 list2 = mapMaybe ((flip lookup) (zip list1 list2))
and now our function works as long as list1 is a list of something for which equality makes sense. Replacement of characters just becomes a special case.
A quick example:
> replace "cat" "dog" "ct"
"dg"
> replace "cat" [1,2,3] "ct"
[1,3]
For two string you may do as follows:
patt :: String -> String -> String -> String
patt (x : xs) (y : ys) p'#(p : ps)
| p == x = y : patt xs ys ps
| otherwise = patt xs ys p'
patt _ _ [] = []
main :: IO ()
main = do
putStrLn $ patt "cat" "dog" "ct"
Related
I'm trying to parse a string "A1B2C3D4" to [('A',1),('B',2),('C',3)] in Haskell.
I'm trying to use a map like this map (\[a, b] -> (a :: Char, b :: Int)) x where x is the string.
This is the function signature I need to follow :: String -> [(Char, Int)].
Unfortunately i'm getting type mismatches, can anyone give any hint how to solve this?
I'm in the right direction?
Well, map is really meant for applying a single function to every element of something, one-by-one. Splitting the string how you want requires context (knowing the next letter), so map isn't the best choice here.
However, you said your solution is required to be in terms of map. It can be done, but it's a bit roundabout. I couldn't think of any way to make map split the actual string, but it can certainly be used to transform it to the correct type:
isDigit :: Char -> Bool
isDigit c = elem c ['0'..'9']
split :: String -> [(Char, Int)]
split str = let chars = filter (not . isDigit) str
nums = filter isDigit str
zipped = zip chars nums in
map (\(a, b) -> (a, read [b])) zipped
There's a few problems.
The pattern [a, b] in map (\[a, b] -> ...) x only matches lists of two elements, so the compiler infers that the function \[a, b] -> ... has type [r] -> s for some r and s.
The compiler knows that map has the type (u -> v) -> [u] -> [v], so it unifies u with [r] and v with s to infer the type [[r]] -> [s] for map (\[a, b] -> ...).
This means x must have type [[r]], that is, it must be a list of lists. But you want x to be a String which is a synonym for [Char]. The compiler can't unify [[r]] and [Char], so it objects.
You're attempting to "cast" a to a Char and b to an Int like you would in C, but you can't do that in Haskell. If you want to convert a Char like '1' into the Int 1, you need a different approach, like read, which you can use to convert from a String to an Int.
Here's some advice. Don't use map. Try writing a recursive solution instead.
Start by considering a few cases:
what does myParser "" return?
what does myParser "a1" return?
what does myParser [a,b] return?
what does myParser (a:b:cs) return?
I came up with this but it's really not safe as it doesn't handle incorrect string like "AA11B2C3"!
splitingN :: Int -> [a] -> [[a]]
splitingN _ [] = []
splitingN n l
| n > 0 = take n l : splitingN n (drop n l)
| otherwise = error "uhhhhhh"
tuplify :: String -> (Char, Int)
tuplify a = (head a, read $ tail a)
stringy :: String -> [(Char, Int)]
stringy s = tuplify <$> splitingN 2 s
> stringy "A1B2C3D4" == [('A',1),('B',2),('C',3),('D',4)]
A much nicer way but still not fully safe would be:
stringy :: [a] -> [(a, a)]
stringy [] = []
stringy (a : b : rest) = (a, b) : splitting rest
stringy [a] = error "uhhhhh"
Should really check if a and b from (a : b : rest) are indeed Char & Int. Also this uses recursion and you mentioned using map so might not suffice and it's pretty polymorphic in it's types.
As others have pointed out, you need to understand that map applies the given function over each member of the list. Once you understand that, you will realize that there is no way you can get the conversion you want by applying a function on the existing list.
This leads to realization that once you have a list of "A1", "B2",... then you can take these and convert it using a map function.
I have given the code for function below. The split' function is not safe as it can blow up in lot of cases (expected a string which can be perfectly split into 2 chars). I am also using the function digitToInt, for which you need to import Data.Char. You did say you want no import, in that case you can write your own digitToInt function, look into the library code, it is fairly straightforward.
import Data.Char
split' :: String -> [String]
split' [] = []
split' (x:y:xs) = (x:[y]) : split' xs
convert :: String -> [(Char, Int)]
convert input = map (\s -> (s!!0 , digitToInt(s!!1) )) $ split' input
I'm trying to write a code in which would take a input string say:
I love bacon, but I love bananas more.
and return ["bacon","bananas"] as output.
However I've ran into some troubles with my code, as I can't seem to properly implement this, currently my idea is that I would input a string and then use word() to split up the string into a string list, and then call getWrods to extract all the words with "ba" as their prefix and then return a list composed of words that start with "ba" for the main function allWords.
My code is as follows:
getWords:: String -> [String] -> [String]
getWords n [] = []
getWords n (x:xs)
| n isPrefixOf x = [x] ++ getWords n xs
|otherwise = getWords n xs
allWordss:: String -> [String]
allWordss n = getWords("ba" words(n))
I think that by using filter :: (a -> Bool) -> [a] -> [a] here, you make the problem easier.
You can as filter condition use - like in your code isPrefixOf :: Eq a => [a] -> [a] -> Bool, but you here wrote in in "infix" notation, but without writing backticks. You thus can call the function with:
isPrefixOf n x
or:
n `isPrefixOf` x
A final problem with your code is that you write:
getWords("ba" words(n))
Here you seem to call a function with brackets, which is quite common in languages like Java, C++, etc. In Haskell however, a function f is called with a parameter x like f x, so you make a call with:
getWords "ba" (words n)
If we use filter here, we thus obtain:
allBaWords :: String -> [String]
allBaWords n = filter (\x -> isPrefixOf "ba" x) (words n)
or shorter:
allBaWords :: String -> [String]
allBaWords = filter (isPrefixOf "ba") . words
We can break up the problem into three logical parts:
Separate a string into a list of words.
Recognize whether a word starts with "ba".
Given a list, get a list of all the elements that satisfy a certain condition (often called a predicate).
Let's start by importing a couple standard modules:
import Data.List (isPrefixOf)
import Data.String (words)
Let's start with (2):
startsWithBa :: String -> Bool
startsWithBa s = -- something using isPrefixOf
As others have noted, you have to enclose isPrefixOf in backticks if you want to use it infix (which most people tend to do so it reads nicely).
Now to separate the string into words, we use
words :: String -> [String]
To extract just the strings that start with "ba", we can use the function
filter :: (a -> Bool) -> [a] -> [a]
I'll let you try to put these pieces together.
I want to write a function pack such that
pack ['a','a','a','b','c','c','a','a','d','e','e','e']
= ["aaa","b","cc","aa","d","eee"]
How can I do this? I'm stuck...
Use Data.List.group:
λ> import Data.List (group)
λ> :t group
group :: Eq a => [a] -> [[a]]
λ> group ['a','a','a','b','c','c','a','a','d','e','e','e']
["aaa","b","cc","aa","d","eee"]
Unless you want to write the function yourself (see Michael Foukarakis answer)
Here's something off the top of my head:
pack :: (Eq a) => [a] -> [[a]]
pack [] = []
-- We split elements of a list recursively into those which are equal to the first one,
-- and those that are not. Then do the same for the latter:
pack (x:xs) = let (first, rest) = span (==x) xs
in (x:first) : pack rest
Data.List already has what you're looking for, though.
I think it's worth adding a more explicit/beginner version:
pack :: [Char] -> [String]
pack [] = []
pack (c:cs) =
let (v, s) = findConsecutive [c] cs
in v : pack s
where
findConsecutive ds [] = (ds, [])
findConsecutive s#(d:ds) t#(e:es)
| d /= e = (s, t)
| otherwise = findConsecutive (e:s) es
If the input is an empty list, the outcome is also an empty list. Otherwise, we find the next consecutive Chars that are equal and group them together into a String, which is returned in the result list. In order to do that we use the findConsecutive auxiliary function. This function's behavior resembles the takeWhile function, with the difference that we know in advance the predicate to use (equality comparison) and that we return both the consumed and the remaining list.
In other words, the signature of findConsecutive could be written as:
findConsecutive :: String -> [Char] -> (String, String)
which means that it takes a string containing only repeated characters to be used as an accumulator and a list whose characters are "extracted" from. It returns a tuple containing the current sequence of elements and the remaining list. Its body should be intuitive to follow: while the characters list is not empty and the current element is equal to the ones in the accumulator, we add the character to the accumulator and recursive into the function. The function returns when we reach the end of the list or a different character is encountered.
The same rationale can be used to understand the body of pack.
I need to change one string from a list of strings (chars if a string is just one letter) for another one from the other list. The signature looks like this:
replace :: [(String, String)] -> String -> String
and I should use functions words/unwords. for e.g. I have [("train","Car")] "John got hit by train." and if I run it the result has to be "John got hit by car.". You see the "train" string was replaced for the "car" one.
I tried everything but I couldn't figure it out.
Can you help me with this?
Please note that the other provided solutions are more idiomatic. Since it sounds like you're being taught and need to structure it this way, here's an example using words/unwords and explicit recursion:
replace :: [(String, String)] -> String -> String
replace a b = unwords $ foldl inner (words b) a
where inner ls tup = replace' tup ls
replace' :: (String, String) -> [String] -> [String]
replace' tup (x:xs)
| x == fst tup = snd tup : replace' tup xs
| otherwise = x : replace' tup xs
replace' _ [] = []
This words approach breaks when you use punctuation, but it works for simple examples:
*Main> replace [("train", "car"), ("dog", "cat")] "dog cannot drive a train"
"cat cannot drive a car"
Let's use map change to apply the change function to each string.
lookup :: Eq a => a -> [(a, b)] -> Maybe b checks a list of pairs for a matching a and gives you Just b if it found one and Nothing otherwise. Let's check the output of lookup oldString changeList and replace the Nothings with the oldString if we get it, but use a newString if we get one:
replace changeList input = map change input where
change oldString = case lookup oldString changeList of
Just newString -> newString
Nothing -> oldString
I'm not certain I understand your question, but you might want
replace xs w w' = map (\(a,b) -> (a,if b == w then w' else b)) xs
For example: replace [(1,2), (3,2), (5,4)] 2 0 => [(1,0), (3,0), (5,4)]. This function generalizes, and should work on strings.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Iterating through a String and replacing single chars with substrings in haskell
I'm trying to implement a function that looks at a String ([Chars]) and checks for every letter whether this letter should be replaced with another string. For example we might have a [Chars] consisting of "XYF" and rules that says "X = HYHY", "Y = OO", then our output should become "HYHYOOF".
I want to use the following two types which I have defined:
type Letters = [Char]
data Rule = Rule Char Letters deriving Show
My idea is that the function should look something like the following below using guards. The problem is however I can't find any information on how to the recursive call should look like when i want browse through all my rules to see if any of them fits to the current letter x. I hope anyone can give some hints on how the notation goes.
apply :: Letters -> [Rule] -> Letters
apply _ _ = []
apply (x:xs) (Rule t r:rs)
| x /= t = apply x (Rule t rs)
| x == t = r++rs:x
| otherwise =
I would suggest a helper function to check whether a rule matches,
matches :: Char -> Rule -> Bool
matches c (Rule x _) = c == x
and then you check for each character whether there are any matching rules
apply :: Letters -> [Rule] -> Letters
apply [] _ = []
apply s [] = s
apply (c:cs) rules = case filter (matches c) rules of
[] -> c : apply cs rules
(Rule _ rs : _) -> rs ++ apply cs rules
If you try an explicit recursion on rules within apply, it will become too ugly, since you need to remember the full rules list for replacing later characters.
I'd suggest that you learn to do this with generic utility functions. Two key functions that you want here:
lookup :: Eq a => a -> [(a, b)] -> Maybe b. Finds a mapping in an association list—a list of pairs used to represent a map or dictionary.
concatMap :: (a -> [b]) -> [a] -> [b]. This is similar to map, but the function mapped over the list returns a list, and the results are concatenated (concatMap = concat . map).
To use lookup you need to change your Rule type to this more generic synonym:
type Rule = (Char, String)
Remember also that String is a synonym for [Char]. This means that concatMap, when applied to String, replaces each character with a string. Now your example can be written this way (I've changed argument orders):
apply :: [Rule] -> String -> String
apply rules = concatMap (applyChar rules)
-- | Apply the first matching rule to the character.
applyChar :: [Rule] -> Char -> String
applyChar rules c = case lookup c rules of
Nothing -> [c]
Just str -> str
-- EXAMPLE
rules = [ ('X', "HYHY")
, ('Y', "OO") ]
example = apply rules "XYF" -- evaluates to "HYHYOOF"
I changed the argument order of apply because when an argument has the same type as the result, it often helps to make that argument the last one (makes it easier to chain functions).
We can go further and turn this into a one-liner by using the utility function fromMaybe :: a -> Maybe a -> a from the Data.Maybe module (fromMaybe default Nothing = default, fromMaybe default (Just x) = x):
import Data.Maybe
apply rules = concatMap (\c -> fromMaybe [c] $ lookup c rules)
An exercise you can do to complement this is to write your version of all of these utility functions on your own by hand: lookup, concatMap (break it down into concat :: [[a]] -> [a] and map :: (a -> b) -> [a] -> [b]), and fromMaybe. That way you can understand the "full stack" involved in this solution.
My solution is structurally similar to the other ones, but uses monads:
import Control.Monad
import Data.Functor
import Data.Maybe
match :: Char -> Rule -> Maybe Letters
match c (Rule c' cs) = cs <$ guard (c == c')
apply :: Letters -> [Rule] -> Letters
apply cs rules =
[s | c <- cs
, s <- fromMaybe [c] $ msum $ map (match c) rules]
The first monad we're dealing with is Maybe a. It is actually a little bit more, a MonadPlus, which allows us to use msum (which boils down something like [Nothing, Just 2, Nothing, Just 3] to the first "hit", here Just 2).
The second monad is [a], which allows us to use a list comprehension in apply.