split string into string in haskell - string

How can I split a string into another string without using Data.List.Split function?
To be more concrete: to turn "Abc" into "['A','b','c']"

If you want literally the string "['A','b','c']" as opposed to the expression ['A','b','c'] which is identical to "Abc" since in Haskell the String type is a synonym for [Char], then something like the following will work:
'[': (intercalate "," $ map show "Abc") ++ "]"
The function intercalate is in Data.List with the type
intercalate :: [a] -> [[a]] -> [a]
It intersperses its first argument between the elements of the list given as its second argument.

I assume you meant how to turn "Abc"into ["A", "b", "c"]. This is quite simple, if the string to split is s, then this will do the trick:
map (\x -> [x]) s

Fire up ghci to see that the expressions you wrote are the same:
Prelude> ['A','b','c']
"Abc"

Related

a Haskell function that converts a list of words to a string

For example:
wordsToString ["all","for","one","and","one","for","all"]
"all for one and one for all"
My code works without a type declaration:
wordsToString [] = ""
wordsToString [word] = word
wordsToString (word:words) = word ++ ' ':(wordsToString words)
But when I do the type check,it shows that it is a list of Chars which seems wrong to me as I'm supposed to declare the input as a list of strings and get a string as the output:
*Main> :type wordsToString
wordsToString :: [[Char]] -> [Char]
I want to change the declaration to wordsToString::[(String)]->[String] but it won't work
I want to change the declaration to wordsToString::[(String)]->[String] but it won't work
No, you want to change the declaration to wordsToString :: [String] -> String. You aren't getting a list of strings out, just a single one.
The function is called concat:
concat :: Foldable t => t [a] -> [a]
concat xs = foldr (++) [] xs
In your case, you want to insert a whitespace between the characters. This function is called intercalate:
intercalate :: [a] -> [[a]] -> [a]
It's defined in terms of intersperse.

Pattern x:xs with strings

I am quite new in Haskell world. I was reading the online http://learnyouahaskell.com but I could not understand a small detail about pattern-matching section. I have written those functions
myFunc' (firstLetter:_) = firstLetter -- returns firstLetter of given string
However if I do something like that
myFunc' (firstLetter:_) = "Hello" ++firstLetter
Gives me following error when I call this function
Couldn't match type ‘Char’ with ‘[Char]’
Expected type: [[Char]]
Actual type: [Char]
But if I modify the function like this
myFunc' (firstLetter:_) = "Hello" ++ [firstLetter]
That works fine when I call this function. I was wondering why do I need brackets in other cases. What is actually firstLetter.
First, if you check the type of (++) in ghci, you get:
Prelude> :t (++)
(++) :: [a] -> [a] -> [a]
That means it takes two lists of a's as arguments.
Likewise let's see what (:) does:
Prelude> :t (:)
(:) :: a -> [a] -> [a]
So the first argument of (:) need not be a list at all. If we fix a == Char we in fact get (:) :: Char -> String -> String.
We can define a function headStr (recall String == [Char]):
headStr :: String -> Char
headStr (x:_) = x
headStr _ = error "Empty string!"
Note that due to the type of (:) in this case x :: Char.
On the other hand if we try to define:
hello :: String -> String
hello (x:_) = "Hello" ++ x
hello _ = error "Empty string!"
it will not type check because in the non error case we get [Char] ++ Char. As ghci helpfully told us, the second argument to (++) must always be a list and in this case since the first argument is [Char] it must also be [Char].
As you noticed yourself, this can be fixed by wrapping x in a list:
hello' :: String -> String
hello' (x:_) = "Hello" ++ [x]
hello' _ = error "Empty string!"
and this works as expected.
"Hello ++ firstLetter
The types there are:
[Char] ++ Char
As you can see, that isn't possible. You can't add a Char to a [Char], they are different types!
But by doing
[firstLetter]
you are creating a list with 1 element, firstLetter. Because firstLetter is a Char, you'll get a list of Chars, i.e. the list is of type [Char].
Adding 2 lists of the same type is allowed, and that's why it works in the second case.

Recursion in Haskell

I'm still learning Haskell and I'm doing a few exercises, but I'm in a jam. So I have a function called "novel" which takes 2 Strings and an Int
(novel :: (String, String, Int) -> String) for its arguments. Novel's input/output must look like the following:
> novel ("Rowling", "Harry Potter", 1998)
"Harry Potter (Rowling, 1998)"
This is my code for my novel function which works as explained above:
novel :: (String, String, Int) -> String
novel (author, book, year) = book ++ " (" ++ author ++ ", " ++ (show year) ++ ")"
I am trying to write a new function called, "cite" (cite :: [(String, String, Int)] -> String). Cite's input/output should look like the following:
> cite [("author1", "book1", year1), ("author2", "book2", year2), ("author3", "book3", year3)]
"book1 (author1, year1)
book2 (author2, year2)
book3 (author3, year3)"
I am trying to use "novel," recursively, in order to get the desired output, but I am not sure how to go about this.
What I've tried:
cite :: [(String, String, Int)] -> String -- | Listed arguments
cite [] = "" -- | Base Case
cite x:xs = [(novel (author, book, year)), (novel (author, book, year)), (novel (author, book, year))]
This is honestly as far as I got. Obviously, it doesn't work, but I am not sure what to do from here.
Perhaps this will give you a head start:
cite :: [(String, String, Int)] -> String
cite [] = ""
cite (x:xs) = undefined -- put your code that recursively calls cite in here, hint: use ++ and "\n\"
The pattern match (x:xs) says this, give me the first item in the list x and the tail of the list xs. It would be the same as writing this:
cite xs' = let x = head xs'
xs = tail xs'
in undefined -- your code here
Or even
cite xs' = undefined -- your code here
where
x = head xs'
xs = tail xs'
Hope that helps push you in the right direction.
EDIT: OP asked for how to do this recursively, below is my original answer:
You should probably re-write your base case to say cite [] = "". It doesn't really make a difference, but it will help with code readability.
Let's start by putting ":t map novel" into ghci to see what you get:
> :t map novel
map novel :: [([Char], [Char], Int)] -> [[Char]]
Which we can rewrite as: map novel :: [(String, String, Int)] -> [String]
How? Because map does a transformation of one type a to another type b and applies it to every item in a list. The first argument of map is any function which takes one argument. Exactly what novel does.
But that doesn't give us what you need, we'll end up with a list of Strings instead of a String:
> cite [("author1", "book1", year1), ("author2", "book2", year2), ("author3", "book3", year3)]
["book1 (author1, year1)","book2 (author2, year2)","book3 (author3, year3)"]
And you would like it to be a single string separated by a newline character "\n". Is there a function that can take a list of strings and concatenate them into one string, but intercalate a separator between them?
First let's describe such a function: String -> [String] -> String. Next we chuck it into Hoogle to see what we get: https://www.haskell.org/hoogle/?hoogle=String+-%3E+%5BString%5D+-%3E+String
Ah, that second function intercalate sounds like what we need. It doesn't just work on Strings, it works on any list. How would it work? Something like this:
> import Data.List (intercalate)
> intercalate "\n" ["List","Of","Strings"]
"List\nOf\nStrings"
So now you can combine intercalate and map to get what you are after. I'll leave the definition for cite up to you.
EDIT: Completely forgot, there is actually a specialised function for this. If you just search for [String] -> String in Hoogle you'll find unlines
There's a reasonably simple way of doing this.
First, map novel to each element of the given list, then use Data.List.intersperse to fill the gaps with newlines. This is my implementation:
import Data.List (intersperse)
cite :: [(String, String, Int)] -> String
cite bs = intersperse '\n' (map novel bs)
Or, in a more elegant points-free style:
cite = intersperse '\n' . map novel
One could also write a nice, efficient recursive function:
cite [] = ""
cite [x] = novel x
cite (x:xs) = novel x ++ '\n' : cite xs
In future problems such as this, keep in mind functions such as map and foldr - these are two of the most integral parts of Haskell and functional programming. Also, your pattern matches need to be enclosed in parentheses.

"Split" returns redundant characters

I'm looking for a simple way of implementing split function. Here is what I have:
import Data.List
groupBy (\x y -> y /= ',') "aaa, bbb, ccc, ddd"
=> ["aaa",", bbb",", ccc",", ddd"]
It's almost what I want except the fact that a delimiter "," and even an extra whitespace are in the result set. I'd like it to be ["aaa","bbb","ccc","ddd"]
So what is the simplest way to do that?
Think about: what is your group separator?
In your case, looks you want to avoid comma and whitespaces, why not?
split :: Eq a => [a] -> [a] -> [[a]]
split separators seq = ...
You can group then writing
groupBy ((==) `on` (flip elem sep)) seq
taking
[ "aaa"
, ", "
, "bbb"
, ", "
, "ccc"
, ", "
, "ddd"
]
and filter final valid groups
filter (not.flip elem sep.head) $ groupBy ((==) `on` (flip elem sep)) seq
returning
["aaa","bbb","ccc","ddd"]
of course, if you want a implemented function, then Data.List.Split is great!
Explanation
This split function works for any a type whenever instance Eq class (i.e. you can compare equality given two a). Not just Char.
A (list-based) string in Haskell is written as [Char], but a list of chars (not a string) is also written as [Char].
In our split function, the first element list is the valid separators (e.g. for [Char] may be ", "), the second element list is the source list to split (e.g. for [Char] may be "aaa, bbb"). A better signature could be:
type Separators a = [a]
split :: Eq a => Separators a -> [a] -> [[a]]
or data/newtype variations but this is another story.
Then, our first argument has the same type as second one - but they are not the same thing.
The resultant type is a list of strings. As a string is [Char] then the resultant type is [[Char]]. If we'd prefer a general type (not just Char) then it becomes [[a]].
A example of splitting with numbers might be:
Prelude> split [5,10,15] [1..20]
[[1,2,3,4],[6,7,8,9],[11,12,13,14],[16,17,18,19,20]]
[5,10,15] is the separator list, [1..20] the input list to split.
(Thank you very much Nick B!)
Have a look at the splitOn function from the Data.List.Split package:
splitOn ", " "aaa, bbb, ccc, ddd" -- returns ["aaa","bbb","ccc","ddd"]
It splits a given list on every occurrence of the complete substring. Alternatively you can also use splitOneOf:
splitOneOf ", " "aaa, bbb, ccc, ddd" -- returns ["aaa","","bbb","","ccc","","ddd"]
Although it returns some empty strings it has the advantage of splitting at one of the characters. The empty strings can be removed by a simple filter.

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