Haskell list comprehension, adding a string between two strings - string

I am having trouble with a couple list problems. The first is supposed to insert a string between strings, as long as the strings are the same length, ie inserts "da" [("so","ed"),("c",""),("",""),("mo","le")] would return ["sodaed" "da" "modale"]
so far I have
inserts :: String -> [(String, String)] -> [String]
inserts str pairs = [[x,str,z] | (x,z) <- pairs, length (x) == length (z)]
inserts' :: String -> [(String, String)] -> [String]
inserts' [] [] = []
inserts' str [(x:xs),(y:ys)]
| (length y) == (length x) = (x, str, y) : inserts' str [xs,ys]
| otherwise = inserts' str [x,ys]
I am getting a type error though matching [char] to string

You're really close! I'm pretty sure the error message you're getting is something different than not being able to match [Char] and String though, because these are the same!
Let's see what happens when we remove the type signature on inserts (I'm doing this in ghci, but you can of course try it via a file as well):
Prelude> let inserts str pairs = [[x,str,z] | (x,z) <- pairs, length x == length z]
Prelude> :t inserts
inserts :: [a] -> [([a], [a])] -> [[[a]]]
OK, that's a pretty general type. As you might know, String is the same as [Char]. So if we substitute Char for a in the type of inserts, and replace [Char] by String, we can see that the inserts can specialize to String -> [(String,String)] -> [[String]].
So the arguments match, but the result has one level of lists too many. That's pretty logical, since x,str and z are strings, so [x,str,z] is a list of strings. All that's needed is to concatenate these three strings into one.
Either you can append the lists 'by hand', using x ++ str ++ z as the expression on the left side of the list comprehension, or you could use concat [x,str,z] to do it; concat flattens a list of lists (of Characters in this case) into a list.
For your second try, you can use something similar instead of the three-tuple of strings (x, str, y), do you see what you need to do?

For the type to be correct, I think for the first function should be:
inserts :: String -> [(String, String)] -> [String]
inserts str pairs = [x ++ str ++z | (x,z) <- pairs, length (x) == length (z)]
or
inserts :: String -> [(String, String)] -> [[String]]
inserts str pairs = [[x,str,z] | (x,z) <- pairs, length (x) == length (z)]
, depending on your needs.

Related

Taking an input string and return a string list of only the words that start with "ba"?

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.

can not get the type of the function just right

I am trying to write a function which take a string and returns the original strings without the space character as a list of string e.g.
toStrings "thanks for your help" -> ["thanks", "for", "your", "help"].
I want to solve this problem using an accumulator so I did the following:
toStrings :: String -> [String]
toStrings str = go str []
where
go str acc
| str == [] = acc
| otherwise = go (dropWhile (/=' ') str) acc : (takeWhile (/=' ') str)
It does not work. The compiler says:
Couldn't match type '[Char]' with 'Char'
I thought I was working with Strings.
Help is much appreciated.
Thanks Eitan
takeWhile on a String will return a String. Therefore, you have
go (…) acc : takeWhile (…)
where the latter is a String. However, you need [String] at that point. Since String = [Char], we have the following type mismatch:
String = [Char] -- actual
[String] = [[Char]] -- expected
GHC then sees the [[Char]] and [Char], removes one list layer, and sees [Char] and Char, which cannot get simplified anymore.
That's why you get your error. Type synonyms and simplified types in error messages.
That being said, you never change the acc, nor do you drop the spaces afterwards. Your current implementation will therefore loop infinitely.
I suggest you to solve this problem without an accumulator and instead try to come up with something similar to
-- pseudo code
go str = firstWord str : go (restOfString str)
Keep in mind that firstWord should strip leading spaces, or you end up with an infinite loop.
I think it helps if you add the type to the go function. Based on the function description it should be:
toStrings :: String -> [String]
toStrings str = go str []
where
go str acc :: String -> [String] -> [String]
| str == [] = acc
| otherwise = go (dropWhile (/=' ') str) acc : (takeWhile (/=' ') str)
But in your recursive call, you call (go somestr acc) : someotherstr (I here use somestr and someotherstr to make it easier to see why it does not work). That does not match, since go somestr acc will result in a [String] (given that works), and someotherstr is a String. If you use the cons (:) it expects the head (left operand) to be a String, and the tail (right operand) to be a [String].
But in fact here we do not need to work with an accumulator at all. We can construct a "cons" and perform recursion at the tail, like:
toStrings :: String -> [String]
toStrings ss | null s1 = []
| otherwise = word : toStrings rest
where s1 = dropWhile (== ' ') ss
(word, rest) = span (/= ' ') s1
So first we drop all the spaces of the string ss, which is then s1. In case s1 is the empty list, then we are done, and we return the empty list. Otherwise we perform a span (a conditional split) such that we obtain a tuple with the word as the first item, and the rest of the string as second item. We then yield the word, and perform recursion on the rest.

Haskell, List as input for a function, How?

I've been given the following question in my coursework;
Define a function
flatten :: [(Char,Int)] -> String
that flattens a list of pairs of characters and digits to a string. For example:
flatten [('a',5),('b',4),('c',2)]
"a5b4c2"
flatten [('d',9),('d',3)]
"d9d3"
My problem is that whenever I attempt to define this function i get a type error relating the the [(Char, Int)] input. For example;
Couldn't match type '(Char, Int)' with '[Char]'
Expected type: [[Char]]
Actual type: [(Char, Int)]
I've tried more ways of writing this definition in more ways than I can count, so I don't have any particular code to show, or any particular error ( I kept getting different ones...so many). All i have so far is;
flatten :: [(Char, Int)] -> String
flatten [] = []
i figure my next line should go something like;
flatten ???? = concat (????)
but I have no idea what to put in place of these question marks and Google search/class notes give no examples to follow.
any ideas?
Well it is clear that in the case the list is not empty, it is of the form ((ca,cb):cs) with ca the Char, cb the Int and cs the remainder of the list [(Char,Int)].
In that case we can simply construct a string for that sequence ca:(show cb) with show :: Show a => a -> String we convert an integer to its String counterpart. Next we concatenate the flattening of remainder of the list to that string, so:
flatten ((ca,cb):cs) = ca:(show cb ++ flatten cs)
Or in full:
flatten :: [(Char, Int)] -> String
flatten [] = []
flatten ((ca,cb):cs) = ca:(show cb ++ flatten cs)
First of all, we try to create a String from a (Char, Int). If we can do that we've almost done, since we can do that for all (Char, Int). So let's transform a single (Char, Int):
flattenSingle :: (Char, Int) -> String
flattenSingle (c, i) = c : show i
Now, we need to do that for all entries:
flattenAll :: [(Char, Int)] -> [String]
flattenAll xs = map flattenSingle xs
And last, but not least, we need to concat the [String] (which is a [[Char]]) to a String (which is a [Char]):
flatten :: [(Char, Int)] -> String
flatten xs = concat (flattenAll xs)
And we're done. How did we do that? Well, we've started with a much easier problem, namely how to get a single String from a (Char, Int), and used that to get our result.
Here's everything in a single function:
flatten = concat . map flattenSingle
where
flattenSingle (c, i) = c : show i
Since concat . map f is often used, there's a function for that, called concatMap:
flatten :: [(Char, Int)] -> String
flatten = concatMap flattenSingle
where
flattenSingle (c, i) = c : show i
Let’s think about what goes into flatten and what comes out of it.
flatten, takes a list of pairs of type: (Char, Int) and produces a [Char]; it produces a list from an existing list. Does this ring a bell?
flatten xs = [ c | (char, int) <- xs, c <-[char] ++ show int]
We can sequentially deconstruct each pair in the given list; for each pair, we turn each component into a string so we can concatenate them. Now we have a string for each pair, we just need to take each character out to produce the final string.
flatten = mconcat. map (\(c,i) -> [c] ++ show i)
You might also use foldl to make your intention very clear:
a=[('a',5),('b',4),('c',2)]
f b (x,y)=b++[x]++(show y)
result=foldl f "" a
Or you can make it a one-liner:
Solution 1:
foldl (\b (x,y)->b++[x]++(show y)) "" a
Solution 2:
concat $ map (\(x,y)->[x]++show y) a
Solution 3: (Being more efficient compared to solution 1)
foldr (\(x,y) b->b++[x]++(show y)) "" a

How to "pack" some strings in a list on Haskell?

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.

Swap characters between strings Haskell

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"

Resources