How to loop through a list of string? - haskell

I suppose to input a list of words. If a word's last letter is the same as the first letter of another word, then we connect them and output a list of lists, which contain all possible connections.
Example: convert(["apple","elephant","eraser", "tower","rank"]) will give
[["elephant", "eraser"], ["tower"], ["rank"], ["rank"], []]
I implemented my code as following:
convert :: [String] -> [[String]]
convert [] = []
convert [x] = []
convert (x : xs) = if last x == head (head xs)
then [head xs] : [] : convert (x : (tail xs))
else convert (x : (tail xs))
But this can only check the first element. How can I loop through the whole list and check each word?
===========================================================================
Ok. I have it done.
convert :: [String] -> [[String]]
convert [] = []
convert (x:xs) = [u | u <- xs, last x == head u] : convert (xs)

Related

Splitting a word in all possible "Subwords"-All possible combinations W/O imports [duplicate]

This question already has an answer here:
All possible sublists of a list
(1 answer)
Closed 3 years ago.
I am trying to get all the possible combinations of a word in the following manner WITHOUT using any imports:
For example...
Input: Bang
Output: [['B','ang'], ['Ba','ng'], ['Ban','g'], ['B','a','ng'], ['B','an','g'], ['Ba','n','g'], ['B','a','n','g']]
This problem has been bothering me for some time and i can't seem to figure out an algorithm to do this..
The code below is what i've done but this gives all the possible combinations of a string but not in the manner that i need.
I tried to implement this python code in haskell but i wasn't able to accomplish it. It basically is the same problem, but you don't have loops in haskell.
Splitting a word into all possible 'subwords' - All possible combinations
The output of the code below is...
["sun","su","s","un","u","n"]
and not
[["s","un"],["s","u","n"],["su","n"]]
-----------------------------------------------------
substring :: String -> [String]
substring [] = []
substring xs = subs xs ++ substring (tail xs)
where
subs xs = foldl step [] xs
step [] a = [[a]]
step acc a = (head acc ++ [a]) : acc
---------------EXAMPLES OF EXPECTED RESULTS BELOW----------------------------------
Input: Bang
Output: [['B','ang'], ['Ba','ng'], ['Ban','g'], ['B','a','ng'], ['B','an','g'], ['Ba','n','g'], ['B','a','n','g']]
Input: Sun
Output: [["s","un"],["s","u","n"],["su","n"]]
Note that the type signature of your attempt is wrong. You want all of the combinations of subword splits, which is a list of list of strings, but your type is just a list of list of strings.
This will work:
onHead :: (a -> a) -> [a] -> [a]
onHead _ [] = []
onHead f (x:xs) = f x:xs
combos :: [a] -> [[[a]]]
combos [] = [[]]
combos [x] = [[[x]]]
combos (x:xs) = [([x]:), onHead (x:)] <*> combos xs
onHead should be self-explanatory: perform the given function on the head of a list. combos recurses as follows: the subwords of a string are the subwords of its tail, with two possibilities for each: either the head is its own subword, or it's tacked onto the beginning of the first subword.
Update: Here's another (IMO cleaner) approach:
combos :: Foldable t => t a -> [[[a]]]
combos = foldr (concatMap . go) [[]]
where go x l = ([x]:l):case l of
[] -> []
h:t -> [(x:h):t]
It's using the same technique as above, just with a cleaner implementation.
Recursion is here to help. Say we have a non-empty list x : xs. We want to know subString (x : xs). We apply our solution recursively to xs, so subString xs is a list of all solutions for xs. However we still have that single x. There are exactly two ways to bring back x in the solution for x : xs which covers for the entire solution set of subString (x : xs):
Bring x back without attaching it to its neighbor. If we have x : xs = "Bang" then x will be 'B' and xs will be "ang" and subString "ang" will be [["ang"],["an","g"],["a","ng"],["a","n","g"]]. This is done by [[x] : u | u <- subString xs]. Here u is a list of Strings, for example ["a","ng"]. As x is a character we must turn it to a String, this is done by [x], attaching it to the head of the list goes by [x] : u, so ["B","a","ng"]. The list comprehension will do it for all elements in subString xs.
Bring x back attaching it to its neighbors. An arbitrary solution of subString xs will look like u : us. We want to attach x to the first element of u : us which is u. So x : u. For example u : us = ["a","n","g"] so u will be "a" and us will be ["n","g"]. Attaching 'B' to "a" is done by 'B' : "a" and will give "Ba". We have to put "Ba back in the list so (x : u) : us. The list comp[rehension looks like [(x : u) : us | (u : us) <- subString xs].
We are still left with the case of a String of a single character. We write [x] for that where x is the single character. So subString [x] will be [[[x]]].
We have to join the solutions together so
subString :: String -> [[String]]
subString [x] = [[[x]]]
subString (x : xs) = [(x : u) : us | (u : us) <- subString xs] ++ [[x] : u | u <- subString xs]
Example
*Main> subString "Bang"
[["Bang"],["Ban","g"],["Ba","ng"],["Ba","n","g"],["B","ang"],["B","an","g"],["B","a","ng"],["B","a","n","g"]]

How do I extend this mergeWords function to any number of strings?

Following is the mergeWords function.
mergeWords [] [] = []
mergeWords [] (y:ys) = y:'\n':(mergeWords [] ys)
mergeWords (x:xs) [] = x:'\n':(mergeWords xs [])
mergeWords (x:xs) (y:ys) = x:y:'\n':(mergeWords xs ys)
If applied on mergeWords "hello" "world" it gives
"hw\neo\nlr\nll\nod\n"
I can't figure out how to extend this to list of strings. Like applying it to 3 strings should first take first character of each of the strings and then put a '\n' and then the second character and so on.
The puzzle is effectively to merge a list of words, a character at a time, into lines with trailing newline characters.
mergeWords :: [String] -> String
We need to take a list like
[ "hello"
, "jim"
, "nice"
, "day"
]
and rearrange it into the lists of things at a given position
[ "hjnd"
, "eiia"
, "lmcy"
, "le"
, "o"
]
That's what the library function transpose does.
And then we need to make a single string which treats those as lines, separated by newlines. Which is what unlines does.
So
mergeWords = unlines . transpose
and we're done.
Sounds reasonably easy if you do it in steps:
cutWords :: [String] -> [[String]] -- ["ab", "cd", "e"] -> [["a", "c", "e"], ["b", "d"]]
concatWord :: [String] -> String -- ["a", "c", "e"] -> "ace\n"
concatWords :: [String] -> String -- use mergeWord on all of them
The most interesting part is of course the cutWords part. What you want there is a zip-like behaviour, and for that it'll help if we "safe" tail and head:
head' (x:xs) = [x]
head' "" = ""
tail' (x:xs) = xs
tail' "" = ""
Now we can implement our cutWords, making sure we stop in time:
cutWords xs = heads : rest
where
heads = map head' xs
tails = map tail' xs
rest = if any (/= "") tails then cutWords tails
else []
Then the remaining part is trivial:
concatWord word = concat word ++ "\n"
concatWords words = concatMap concatWord word

Ordered occurrences of characters in string

I want to get all the occurrences of the characters I have in a string.
So for example if I have the string "nunddrdr" I want to get "nnudddrr".
This is how the code looks like which should achieve this:
usort :: String -> String
l :: String
l = ""
usort (x:xs)
| xs == [] = l ++ [x]
| otherwise = l ++ [n | n <- (x:xs), n = x] ++ usort (xs)
It doesn't work correctly because it also proccesses already used characters.
You have correctly observed that your recursive calls to usort are not removing the characters that have already been used, so you will need to modify the function so that it does this. You can use filter to select only the elements of the list that match some criteria, so we can remove the already used characters like so:
filter (\=x) xs
We can select all of the characters that match x in the same manner, by using
filter (==x) xs
You can use these two things to create your usort function:
usort (x:xs) = x : (filter (==x) xs) ++ usort (filter (/=x) xs)
However, you have failed to consider the case when you will be trying to sort an empty list, so you will also need to add:
usort [] = []
Putting this together, you get
usort::(Eq a)=>[a]->[a]
usort [] = []
usort (x:xs) = x : filter (==x) xs ++ usort (filter (/=x) xs)

replace character to number in haskell

I have function change which replace some characters to numbers. Here it is:
change [] = []
change (x:xs) | x == 'A' = '9':'9':change xs
| x == 'B' = '9':'8':change xs
| otherwise = change xs
and the output is:
Main> change "aAB11s"
"9998"
but I need this:
Main> change "aAB11s"
"a999811s"
How can I do this?
Try this:
change [] = []
change (x:xs) | x == 'A' = '9':'9':change xs
| x == 'B' = '9':'8':change xs
| otherwise = x:change xs
The only change is in otherwise.
In addition to #kostya 's answer, you don't need to write the recursive part youself, try this out:
change :: String -> String
change xs = concatMap chToStr xs
where chToStr 'A' = "99"
chToStr 'B' = "98"
chToStr x = [x]
or, more point-freely (actually this is preferred if the point-free refactoring doesn't hurt the readability):
change :: String -> String
change = concatMap chToStr
where chToStr 'A' = "99"
chToStr 'B' = "98"
chToStr x = [x]
And you can test the result:
λ> change "aAB11s"
"a999811s"
Some explanation:
It's tempting to do an elementwise replacement by passing map a function
f :: Char -> Char. But here you can't do that because for A, you want two characters, i.e. 99, so the function you want is of type Char -> String (String and [Char] in Haskell are equivalent) which does not fit the type signature.
So the solution is to also wrap other characters we don't care about into lists, and afterwards, we can perform a string concatenation(this function in Haskell is called concat) to get a string back.
Further, concatMap f xs is just a shorthand for concat (map f xs)
λ> map (\x -> [x,x]) [1..10]
[[1,1],[2,2],[3,3],[4,4],[5,5],[6,6],[7,7],[8,8],[9,9],[10,10]]
λ> concat (map (\x -> [x,x]) [1..10])
[1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10]
λ> concatMap (\x -> [x,x]) [1..10]
[1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10]

Haskell: String splitting. Learning algo

I've seen the other threads about missing split function but I didn't want to peek as I do this for learning purposes and want to figure out myself. So here it is:
split :: Char -> String -> [String]
split c xs | null f = []
| otherwise = f : split c s'
where (f,s) = break (== c) xs
s' | null s = s
| otherwise = tail s
It seems to work fine (please tell me if anything is wrong with it) but when I use a splitting character that is not in the string then the function returns a list with a single element of the original string whereas I want it to return an empty list. I can't figure out how to do it.
Any ideas?.
You can simply write a wrapper function, changing the result of your original one:
split' x xs = go (split x xs) where
go [_] = []
go ys = ys
There are many ways to write a split function, e.g.
split x xs = foldl' go [[]] xs where
go (ys:yss) y | y == x = []:ys:yss
| otherwise = (y:ys):yss
or
import Data.List
split x xs = map tail $ groupBy (const (/=x)) (x:xs)

Resources