Given a list of tuples asd :: [(Char, Char)], I want to write a function that takes in a string and returns the same string, with characters matching the first element of tuples in asd replaced with the corresponding second element.
For example, with asd = [('a', 'b'), ('c', 'd')], with input "ac", it should return "bd".
I also want to write a function that does the reverse, which when given input "bd" should return "ac".
I have a solution, but I can't use list generator and recursion.
This is my solution:
xyz :: String -> String
xyz x = concat (map y x) where y ys = [a | (b,a) <- asd, ys == b]
zyx x :: String -> String
zyx x = concat (map y x) where y ys = [a | (a,b) <- asd, ys == b]
How can I write this without recursion and list generator?
Pending clarification regarding exact specification in comments, I am assuming that you want to replace characters while keeping non-matching as-is.
To apply a function to each element of a list (a String is just [Char]), use map :: (a -> b) -> [a] -> [b]. Hence, you will need to write a map on the input string like so
xyz :: String -> String
xyz = map replace
This replace function should run through the list of tuples asd, and find the first tuple that matches. (I'm making an assumption here as it wasn't specified how to handle having more than one matching tuple.) To do this, we make use of filter :: (a -> Bool) -> [a] -> [a] to find which tuples are matching.
replace :: Char -> Char
replace c = case filter ((== c) . fst) asd of
((_, c'):_) -> c' -- found a replacement
[] -> c -- no match, don't replace character
where ((== c) . fst) compares the first element of the tuple to the character c.
Lastly, to implement the reverse function, simply do the same thing but with the replacement looking up by the second element instead, like so
zyx :: String -> String
zyx = map replace'
replace' :: Char -> Char
replace' c = case filter ((== c) . snd) asd of
((c', _):_) -> c' -- found a replacement
[] -> c -- no match, don't replace character
Reference links for fst and snd.
Related
The next lines should show how its has to work..
[14,2,344,41,5,666] after [(14,2),(2,1),(344,3),(5,1),(666,3)]
["Zoo","School","Net"] after [("Zoo",3),("School",6),("Net",3)]
Thats my code up to now
zipWithLength :: [a] -> [(a, Int)]
zipWithLength (x:xs) = zipWith (\acc x -> (x, length x):acc) [] xs
I want to figure out what the problem in the second line is.
If you transform the numbers into strings (using show), you can apply length on them:
Prelude> let zipWithLength = map (\x -> (x, length (show x)))
Prelude> zipWithLength [14,2,344,41,5,666]
[(14,2),(2,1),(344,3),(41,2),(5,1),(666,3)]
However, you cannot use the same function on a list of strings:
Prelude> zipWithLength ["Zoo","School","Net"]
[("Zoo",5),("School",8),("Net",5)]
The numbers are not the lengths of the strings, but of their representations:
Prelude> show "Zoo"
"\"Zoo\""
Prelude> length (show "Zoo")
5
As noted in the comments, similar problems may happen with other types of elements:
Prelude> zipWithLength [(1.0,3),(2.5,3)]
[((1.0,3),7),((2.5,3),7)]
Prelude> show (1.0,3)
"(1.0,3)"
Prelude> length (show (1.0,3))
7
If you want to apply a function on every element of a list, that is a map :: (a -> b) -> [a] -> [b]. The map thus takes a function f and a list xs, and generates a list ys, such that the i-th element of ys, is f applied to the i-th element of xs.
So now the only question is what mapping function we want. We want to take an element x, and return a 2-tuple (x, length x), we can express this with a lambda expression:
mapwithlength = map (\x -> (x, length x))
Or we can use ap :: Monad m => m (a -> b) -> m a -> m b for that:
import Control.Monad(ap)
mapwithlength = map (ap (,) length)
A problem is that this does not work for Ints, since these have no length. We can use show here, but there is an extra problem with that: if we perform show on a String, we get a string literal (this means that we get a string that has quotation marks, and where some characters are escaped). Based on the question, we do not want that.
We can define a parameterized function for that like:
mapwithlength f = map (ap (,) (length . f))
We can basically leave it to the user. In case they want to work with integers, they have to call it with:
forintegers = mapwithlength show
and for Strings:
forstrings = mapwithlength id
After installing the number-length package, you can do:
module Test where
import Data.NumberLength
-- use e.g for list of String
withLength :: [[a]] -> [([a], Int)]
withLength = map (\x -> (x, length x))
-- use e.g for list of Int
withLength' :: NumberLength a => [a] -> [(a, Int)]
withLength' = map (\x -> (x, numberLength x))
Examples:
>>> withLength ["Zoo", "bear"]
[("Zoo",3),("bear",4)]
>>> withLength' [14, 344]
[(14,2),(344,3)]
As bli points out, calculating the length of a number using length (show n) does not transfer to calculating the length of a string, since show "foo" becomes "\"foo\"". Since it is not obvious what the length of something is, you could parameterise the zip function with a length function:
zipWithLength :: (a -> Int) -> [a] -> [(a, Int)]
zipWithLength len = map (\x -> (x, len x))
Examples of use:
> zipWithLength (length . show) [7,13,666]
[(7,1),(13,2),(666,3)]
> zipWithLength length ["Zoo", "School", "Bear"]
[("Zoo",3),("School",6),("Bear",4)]
> zipWithLength (length . concat) [[[1,2],[3],[4,5,6,7]], [[],[],[6],[6,6]]]
[([[1,2],[3,4],[5,6,7]],7),([[],[],[6],[6,6]],3)]
I'd like to generate all words from a given alphabet. For example:
['a', 'b'] -> ["","a","b","aa","ba","ab","bb","aaa","baa","aba","bba","aab" ...]
I manage to implement this function by now:
myfunc :: [a] -> [[[a]]]
myfunc l = iterate fromList $ map (\x -> x : []) l
where
fromList ls = [y : ys | y <- l, ys <- ls]
But this function doesn't generate the result correctly. I want the answer to be a String to take for example only the first 5 elements -> ["","a","b","aa","ba"]. Any help how to do that?
Based on your example you do not generate sublists: you generate sequences of string from the given alphabet l.
Furthermore the signature of myfunc should be [a] -> [[a]]. Indeed the elements of the list are strings as well, so [a]s.
You can use recursive list comprehension for that:
myfunc :: [a] -> [[a]]
myfunc l = []:[(x:ys) | ys <- myfunc l, x <- l]
Which generates:
*Main> take 10 $ myfunc "ab"
["","a","b","aa","ba","ab","bb","aaa","baa","aba"]
The code works as follows, the first element we emit is the empty string (the [] part in the []:...). So this will be the first result we emit (but also the first in the recursive result).
Now in the list comprehension part, we iterate over all elements of myfunc l (so first ys is the empty list), and we prepend that element with all characters of the alphabet (a and b). Next ys will be [a] and so we prepend the characters of the alpabeth with that, and so on.
Is there any way I can split String in Haskell on the last occurrence of given character into 2 lists?
For example I want to split list "a b c d e" on space into ("a b c d", "e").
Thank you for answers.
I'm not sure why the solutions suggested are so complicated. Only one two traversals are needed:
splitLast :: Eq a => a -> [a] -> Either [a] ([a],[a])
splitLast c' = foldr go (Left [])
where
go c (Right (f,b)) = Right (c:f,b)
go c (Left s) | c' == c = Right ([],s)
| otherwise = Left (c:s)
Note this is total and clearly signifies its failure. When a split is not possible (because the character specified wasn't in the string) it returns a Left with the original list. Otherwise, it returns a Right with the two components.
ghci> splitLast ' ' "hello beautiful world"
Right ("hello beautiful","world")
ghci> splitLast ' ' "nospaceshere!"
Left "nospaceshere!"
Its not beautiful, but it works:
import Data.List
f :: Char -> String -> (String, String)
f char str = let n = findIndex (==char) (reverse str) in
case n of
Nothing -> (str, [])
Just n -> splitAt (length str - n -1) str
I mean f 'e' "a b c d e" = ("a b c d ", "e"), but I myself wouldn't crop that trailing space.
I would go with more pattern matching.
import Data.List
splitLast = contract . words
where contract [] = ("", "")
contract [x] = (x, "")
contract [x,y] = (x, y)
contract (x:y:rest) = contract $ intercalate " " [x,y] : rest
For long lists, we just join the first two strings with a space and try the shorter list again. Once the length is reduced to 2, we just return the pair of strings.
(x, "") seemed like a reasonable choice for strings with no whitespace, but I suppose you could return ("", x) instead.
It's not clear that ("", "") is the best choice for empty strings, but it seems like a reasonable alternative to raising an error or changing the return type to something like Maybe (String, String).
I can propose the following solution:
splitLast list elem = (reverse $ snd reversedSplit, reverse $ fst reversedSplit)
where
reversedSplit = span (/= elem) $ reverse list
probably not the fastest one (two needless reverses) but I like it's simplicity.
If you insist on removing the space we're splitting on, you can go for:
import qualified Data.List as List
splitLast list elem = splitAt (last $ List.elemIndices elem list) list
however, this version assumes that there will be at least one element matching the pattern. If you don't like this assumption, the code gets slightly longer (but no double-reversals here):
import qualified Data.List as List
splitLast list elem = splitAt index list where
index = if null indices then 0 else last indices
indices = List.elemIndices elem list
Of course, choice of splitting at the beginning is arbitrary and probably splitting at the end would be more intuitive for you - then you can simply replace 0 with length list
My idea is to split at every occurrence and then separate the initial parts from the last part.
Pointed:
import Control.Arrow -- (&&&)
import Data.List -- intercalate
import Data.List.Split -- splitOn
breakOnLast :: Eq a => a -> [a] -> ([a], [a])
breakOnLast x = (intercalate x . init &&& last) . splitOn x
Point-free:
liftA2 (.) ((&&& last) . (. init) . intercalate) splitOn
(.) <$> ((&&&) <$> ((.) <$> pure init <*> intercalate) <*> pure last) <*> splitOn
I am new to Haskell and functional programming in general. I am trying to implement a function to take a list like this
["abc", "def", "ghi"]
and want to be able to replace the xth character in the yth element for example
replaceChar 1 2 'd' arr
would produce
["abc", "ded", "ghi"]
So essentially the first parameter is the element and the second is the position of the string, the third is the character and last is the [String].
The signature of this function looks like this:
replaceChar :: Int -> Int -> Char -> [String] -> [String]
Any help would be appreciated. Thanks!
First a note: while your signature is perfectly fine, you really don't use the fact that you're dealing with character strings, it could just as well be lists of any other type. It's usually a good idea1 to manifest that in your signature by using a completely generic type variable (lowercase letter) instead of Char:
replaceAtXY :: Int -> Int -> a -> [[a]] -> [[a]]
Next, note that basically the problem can be reduced to modifying the n-th element of an ordinary (non-nested) lists. On the outer list, you modify the y-th sublist, namely, in that sublist you modify the x-th element.
So what does "modifying" mean in Haskell? We can't mutate elements of course2. We need a function that takes a list and returns another one, and does this based on a function which operates on single elements of the list.
modifyNth :: Int -> (a->a) -> [a]->[a]
Observe that this is somewhat similar to the standard function map :: (a->b) -> [a]->[b].
Once you have that function, you can easily implement
modifyXY :: Int -> Int -> (a->a) -> [[a]]->[[a]]
modifyXY x y f nList = modifyNth y (modifyNth x f) nList
(BTW the nList parameter doesn't need to be written, you can η-reduce it).
1As to why this is a good idea: obviously, it allows you to use the function in more general settings. But more importantly, it gives the type checker extra information that you won't do anything with the contained elements themselves. This actually helps to catch a lot of bugs in more complicated applications!
2Actually you can, even with rather nice semantics, in the ST monad.
Let's break this problem into two functions, one that replaces an element in a string with a new char, and one that does this for a list of strings.
I would recommend something like:
replaceCharInStr :: Int -> Char -> String -> String
replaceCharInStr 0 c (s:ss) = c:ss
replaceCharInStr n c (s:ss) = s : ???
replaceCharInStr n c [] = error "replaceCharInStr: Empty string"
here we say that if n is 0, ignore the first element of the string with c, then if n is not 0 and the list has at least one element, prepend that element in front of something (exercise left to reader. Hint: recursion), then if our string is empty, raise an error. I will say that I don't particularly like that error is used here, it would be much better to return a Maybe String, or we could say that replaceCharInStr n c [] = [c]. We could also change the type signature to replaceCharInStr :: Int -> a -> [a] -> [a], since this isn't specific to strings.
For the next function, what we'd like to do is take an index, and apply a function at that index. In general, this function would have type
applyAt :: Int -> (a -> a) -> [a] -> [a]
And could be implemented similarly to replaceCharInStr with
applyAt :: Int -> (a -> a) -> [a] -> [a]
applyAt 0 f (x:xs) = f x : xs
applyAt n c (x:xs) = x : ???
applyAt n c [] = error "applyAt: Empty list"
In fact, this is the exact same shape as replaceCharInStr, so if you get this one implemented, then you should be able to implement replaceCharInStr in terms of applyAt as
replaceCharInStr n c xs = applyAt n (\x -> c) xs
-- Or = applyAt n (const c) xs
Then your replaceChar function could be implemented as
replaceChar :: Int -> Int -> Char -> [String] -> [String]
replaceChar n m c strings = applyAt n (replaceCharInStr m c) strings
-- Or = applyAt n (applyAt m (const c)) strings
All that's left is to implement applyAt.
If you have Edward Kmett's Lens package, then your example is a one-liner:
import Control.Lens
["abc", "def", "ghi"] & ix 1 . ix 2 .~ 'd'
returns
["abc","ded","ghi"]
Lens can emulate the indexing and property access you'd expect from an imperative language, but in Haskell. If you're just beginning to learn Haskell, you should probably wait a bit before using Lens. It's clever and powerful but it's also large and complex.
Try this:
replace n 0 c (x:xs) = (replace' n c x) : xs
replace n m c (x:xs) = x : (replace n (m-1) c xs)
where
replace' 0 c (x:xs) = c : xs
replace' n c (x:xs) = x : (replace' (n-1) c xs)
Here you just traverse the list until the corresponding index is 0 and we replace the character in the matches list. We use the same principle for replacing the charachter in the list. We traverse it and when we reach the specified index, we replace the character at that index by our new one.
In the end, everything gets consed bak on each other to replace the old structure, this time with the character replaced.
Currently working with Haskell on a function that takes a String in parameters and return a list of (Char, Int) The function occur works with multiple type and is used in the function called word.
occur::Eq a=>a->[a]->Int
occur n [] = 0
occur n (x:xs) = if n == x
then 1 + occur n xs
else occur n xs
word::String->[(String,Int)]
word xs = [(x,y) | x<-head xs, y<-(occur x xs)]
Get me this error
ERROR "file.hs":31 - Type error in generator
*** Term : head xs
*** Type : Char
*** Does not match : [a]
What am I doing wrong ? How can I make this code run properly , type-wise ?
The problem is you say that xs has type String, so head xs has type Char, and then you try to iterate over a single Char, which can't be done. The a <- b syntax only works when b is a list. You have the same problem in that y <- occur x xs is trying to iterate over a single Int, not a list of Int. You also had a problem in your type signature, the first type in the tuple should be Char, not String. You can fix it with:
word :: String -> [(Char, Int)]
word xs = [(x, occur x xs) | x <- xs]
Here we loop over the entire string xs, and for each character x in xs we compute occur x xs.
I would actually recommend using a slightly stronger constraint than just Eq. If you generalize word (that I've renamed to occurrences) and constrain it with Ord, you can use group and sort, which allow you to keep from iterating over the list repeatedly for each character and avoid the O(n^2) complexity. You can also simplify the definition pretty significantly:
import Control.Arrow
import Data.List
occurrences :: Ord a => [a] -> [(a, Int)]
occurrences = map (head &&& length) . group . sort
What this does is first sort your list, then group by identical elements. So "Hello, world" turns into
> sort "Hello, world"
" ,Hdellloorw"
> group $ sort "Hello, world"
[" ", ",", "H", "d", "e", "lll", "oo", "r", "w"]
Then we use the arrow operator &&& which takes two functions, applies a single input to both, then return the results as a tuple. So head &&& length is the same as saying
\x -> (head x, length x)
and we map this over our sorted, grouped list:
> map (head &&& length) $ group $ sort "Hello, world"
[(' ',1),(',',1),('H',1),('d',1),('e',1),('l',3),('o',2),('r',1),('w',1)]
This eliminates repeats, you aren't having to scan the list over and over counting the number of elements, and it can be defined in a single line in the pointfree style, which is nice. However, it does not preserve order. If you need to preserve order, I would then use sortBy and the handy function comparing from Data.Ord (but we lose a nice point free form):
import Control.Arrow
import Data.List
import Data.Ord (comparing)
occurrences :: Ord a => [a] -> [(a, Int)]
occurrences = map (head &&& length) . group . sort
occurrences' :: Ord a => [a] -> [(a, Int)]
occurrences' xs = sortBy (comparing ((`elemIndex` xs) . fst)) $ occurrences xs
You can almost read this as plain English. This sorts by comparing the index in xs of the first element of the tuples in occurrences xs. Even though elemIndex returns a value of type Maybe Int, we can still compare those directly (Nothing is "less than" any Just value). It simply looks up the first index of each letter in the original string and sorts by that index. That way
> occurrences' "Hello, world"
returns
[('H',1),('e',1),('l',3),('o',2),(',',1),(' ',1),('w',1),('r',1),('d',1)]
with all the letters in the original order, up to repetition.