Haskell map list of tuples to list of tuples - list

I'm trying to map a list of tuples into a different list of tuples with no luck.
Example input:
a = [("eo","th"),("or","he")]
Example output:
[('e','t'),('o','h'),('o','h'),('r','e')]
I have tried:
map (\(a,b) -> (a!!0,b!!0):(a!!1,b!!1):[]) a
but it produces:
[[('e','t'),('o','h')],[('o','h'),('r','e')]]

You have to use concat on your result or use concatMap instead of map. After all, you return lists in your map and therefore get a list of lists.
Let's give your function a name and a type:
magic :: [([Char], [Char])] -> [(Char, Char)]
Now, we can think of this as a two-step process: from every pair in the original list we're going to get a list:
magicPair :: ([Char], [Char]) -> [(Char, Char)]
magicPair (a,b) = zip a b
Now we need to map magicPair over all elements in your original list and concatenate the result:
magic xs = concat (map magicPair xs)
The combination concat . map f is so common that there is a function called concatMap for this:
magic xs = concatMap magicPair xs
And using a function f on a pair instead of two arguments is also common, so magicPair = uncurry zip:
magic xs = concatMap (uncurry zip) xs
We can now remove xs on both sides to end up with the final variant of magic:
magic = concatMap (uncurry zip)

Here is a quick way to give you the output
simplify = (>>= uncurry zip)

Related

Get the first elements of a list of tuples

I have this list of tuples
[(4,'a'), (1,'b'), (2,'c'), (2,'a'), (1,'d'), (4,'e')]
I want to get the first elements of every tuple then replicate it to make the following: "aaaabccaadeeee"
I came up with this code, but it only gives me the replicate of the first tuple.
replicate (fst ( head [(4,'a'), (1,'b')])) ( snd ( head [(4,'a'), (1,'b')]))
--output is: "aaaa"
I was thinking to use map for to get the replicate of every tuple, but I didn't succeed.
Since you already know how to find the correct answer for a single element, all you need is a little recursion
func :: [(Int, a)] -> [a]
func [] = []
func ((n, elem):rest) = (replicate n elem) ++ (func rest)
Mapping the values should also work. You just need to concatenate the resulting strings into one.
func :: [(Int, a)] -> [a]
func xs = concat $ map func2 xs where
func2 (n, elem) = replicate n elem
Or, if you are familiar with currying:
func :: [(Int, a)] -> [a]
func xs = concat $ map (uncurry replicate) xs
Finally, if you are comfortable using function composition, the definition becomes:
func :: [(Int, a)] -> [a]
func = concat . map (uncurry replicate)
Using concat and map is so common, there is a function to do just that. It's concatMap.
func :: [(Int, a)] -> [a]
func = concatMap (uncurry replicate)
Let
ls = [(4,'a'), (1,'b'), (2,'c'), (2,'a'), (1,'d'), (4,'e')]
in
concat [replicate i x | (i, x) <- ls]
will give
"aaaabccaadeeee"
The point-free version
concat . map (uncurry replicate)
You are correct about trying to use map. But first lets see why your code did not work
replicate (fst ( head [(4,'a'), (1,'b')])) ( snd ( head [(4,'a'), (1,'b')]))
Your first parameter to replicate is the head of your list which is (4, 'a'). Then you are calling fst on this, thus the first parameter is 4. Same things happens with second parameter and you get 'a'. The result of which you see.
Before using map lets try to do this with recursion. You want to take one element of list and apply replicate to it and then combine it with the result of applying replicate on the second element.
generate [] = []
generate (x:xs) = replicate (fst x) (snd x) ++ generate xs
Do note I am using pattern matching to get the first element of list. You can us the pattern matching to get the element inside the tuple as well, and then you would not need to use the fst/snd functions. Also note I am using pattern matching to define the base case of empty list.
generate [] = []
generate ((x,y):xs) = replicate x y ++ generate xs
Now coming to map, so map will apply your function to every element of the list, here's the first try
generate (x,y) = replicate x y
map generate xs
The result of the above will be slightly different from recursion. Think about it, map is going to apply generate to every element and store the result in a list. generate creates a list. So when you apply map you are creating a list of list. You can use concat to flatten it if you want, which will give you the same result as recursion.
Last thing, if you can use recursion, then you can use fold as well. Fold will just apply a function to every element of the list and return the accumulated results (broadly speaking).
--first parameter is the function to apply, second is the accumulator, third is your list
foldr step [] xs
where step (x,y) acc =
(replicate x y) ++ acc
Again here I have used pattern matching in the function step to extract the elements of the tuple out.

How to compare elements in a [[]]?

I am dealing with small program with Haskell. Probably the answer is really simple but I try and get no result.
So one of the part in my program is the list:
first = [(3,3),(4,6),(7,7),(5,43),(9,9),(32,1),(43,43) ..]
and according to that list I want to make new one with element that are equal in the () =:
result = [3,7,9,43, ..]
Even though you appear to have not made the most minimal amount of effort to solve this question by yourself, I will give you the answer because it is so trivial and because Haskell is a great language.
Create a function with this signature:
findIdentical :: [(Int, Int)] -> [Int]
It takes a list of tuples and returns a list of ints.
Implement it like this:
findIdentical [] = []
findIdentical ((a,b) : xs)
| a == b = a : (findIdentical xs)
| otherwise = findIdentical xs
As you can see, findIdentical is a recursive function that compares a tuple for equality between both items, and then adds it to the result list if there is found equality.
You can do this for instance with list comprehension. We iterate over every tuple f,s) in first, so we write (f,s) <- first in the right side of the list comprehension, and need to filter on the fact that f and s are equal, so f == s. In that case we add f (or s) to the result. So:
result = [ f | (f,s) <- first, f == s ]
We can turn this into a function that takes as input a list of 2-tuples [(a,a)], and compares these two elements, and returns a list [a]:
f :: Eq a => [(a,a)] -> [a]
f dat = [f | (f,s) <- dat, f == s ]
An easy way to do this is to use the Prelude's filter function, which has the type definition:
filter :: (a -> Bool) -> [a] -> [a]
All you need to do is supply predicate on how to filter the elements in the list, and the list to filter. You can accomplish this easily below:
filterList :: (Eq a) => [(a, a)] -> [a]
filterList xs = [x | (x, y) <- filter (\(a, b) -> a == b) xs]
Which behaves as expected:
*Main> filterList [(3,3),(4,6),(7,7),(5,43),(9,9),(32,1),(43,43)]
[3,7,9,43]

Joining lists in Haskell

So i've been praticing Haskell, and i was doing just fine, until i got stuck in this exercise. Basically i want a function that receives a list like this :
xs = [("a","b"),("a","c"),("b","e")]
returns something like this :
xs = [("a",["b","c"]), ("b",["e"])].
I come up with this code:
list xs = [(a,[b])|(a,b) <- xs]
but the problem is that this doesn't do what i want. i guess it's close, but not right.
Here's what this returns:
xs = [("a",["b"]),("a",["c"]),("b",["e"])]
If you don't care about the order of the tuples in the final list, the most efficient way (that doesn't reinvent the wheel) would be to make use of the Map type from Data.Map in the containers package:
import Data.Map as Map
clump :: Ord a => [(a,b)] -> [(a, [b])]
clump xs = Map.toList $ Map.fromListWith (flip (++)) [(a, [b]) | (a,b) <- xs]
main = do print $ clump [("a","b"),("a","c"),("b","e")]
If you do care about the result order, you'll probably have to do something ugly and O(n^2) like this:
import Data.List (nub)
clump' :: Eq a => [(a,b)] -> [(a, [b])]
clump' xs = [(a, [b | (a', b) <- xs, a' == a]) | a <- nub $ map fst xs]
main = do print $ clump' [("a","b"),("a","c"),("b","e")]
You could use right fold with Data.Map.insertWith:
import Data.Map as M hiding (foldr)
main :: IO ()
main = print . M.toList
$ foldr (\(k, v) m -> M.insertWith (++) k [v] m)
M.empty
[("a","b"),("a","c"),("b","e")]
Output:
./main
[("a",["b","c"]),("b",["e"])]
The basic principle is that you want to group "similar" elements together.
Whenever you want to group elements together, you have the group functions in Data.List. In this case, you want to specify yourself what counts as similar, so you will need to use the groupBy version. Most functions in Data.List have a By-version that lets you specify more in detail what you want.
Step 1
In your case, you want to define "similarity" as "having the same first element". In Haskell, "having the same first element on a pair" means
(==) `on` fst
In other words, equality on the first element of a pair.
So to do the grouping, we supply that requirement to groupBy, like so:
groupBy ((==) `on` fst) xs
This will get us back, in your example, the two groups:
[[("a","b"),("a","c")]
,[("b","e")]]
Step 2
Now what remains is turning those lists into pairs. The basic principle behind that is, if we let
ys = [("a","b"),("a","c")]
as an example, to take the first element of the first pair, and then just smash the second element of all pairs together into a list. Taking the first element of the first pair is easy!
fst (head ys) == "a"
Taking all the second elements is fairly easy as well!
map snd ys == ["b", "c"]
Both of these operations together give us what we want.
(fst (head ys), map snd ys) == ("a", ["b", "c"])
Finished product
So if you want to, you can write your clumping function as
clump xs = (fst (head ys), map snd ys)
where ys = groupBy ((==) `on` fst) xs

Multiple functions on one list?

i have a list
[1,1,1,1,1]
and i am trying to write function which will return list
[2,3,4,5,6]
i want to use function map like this
map (+1) [1,1,1,1,1]
which will return
[2,2,2,2,2]
after that i want to call map function on last four elements of returned list so after i get [2,2,2,2,2] i want to use map on last four [2,2,2,2] that will return [3,3,3,3] and replace last four elements from first map call so i get [2,3,3,3,3] etc..
map (+1)[1,1,1,1,1]
map (+1) [2,2,2,2]
map (+1) [3,3,3]
map (+1) [4,4]
map (+1) [5]
returned:
[2,2,2,2,2]
[2,3,3,3,3]
[2,3,4,4,4]
[2,3,4,5,5]
[2,3,4,5,6]
i need to return only last list...
btw this is only Simplified version, originaly i have list of lists ... i just cant figure how to call function how i described..
thanks.
I think you want something like
mapTails f [] = []
mapTails f (x:xs) = f x : mapTails f (map f xs)
IMO the most elegant way would be
zipWith($) $ iterate((+1).) id
scanl almost does what you want:
Prelude> scanl (+) 1 [1,1,1,1,1]
[1,2,3,4,5,6]
You could drop the first item, which is just the initial state value we're passing in:
Prelude> tail $ scanl (+) 1 [1,1,1,1,1]
[2,3,4,5,6]
You can accomplish what you're looking for using a recursive function, instead:
myFn :: Num a => [a] -> [a]
myFn [] = []
myFn (x:xs) = x + 1 : (myFn $ map (+1) xs)
main = print $ myFn [1,1,1,1,1] -- Prints [2,3,4,5,6]
See http://codepad.org/wBwynlGt
Would something like this do what you want?
startList = [1,1,1,1] -- orwhatever you want it to be
map (\(x,i) -> x + i) $ zip startList [1..]
The zip basically pairs each element in the list with what you want to add to it, and the map function then adds each element in the list to that value to get the result you want.
Your algorithm version O(n2) time:
plusSlow :: [Int] -> [Int]
plusSlow [] = []
plusSlow (x:xs) = (head mapped):(plusSlow $ tail mapped)
where mapped = map (+1) (x:xs)
Faster version O(n) time:
plusFast :: [Int] -> [Int]
plusFast x = pf x 1
pf :: [Int] -> Int -> [Int]
pf [] _ = []
pf (x:xs) n = (x+n):(pf xs (n+1))

Flatten a list of lists

I have to write a function that flattens a list of lists.
For example flatten [] = [] or flatten [1,2,3,4] = [1,2,3,4] or flatten [[1,2],[3],4,5]] = [1,2,3,4,5]
I'm having trouble with the being able to match the type depending on what is given to the flatten function.
Here's what I have:
data A a = B a | C [a] deriving (Show, Eq, Ord)
flatten::(Show a, Eq a, Ord a)=>A a -> A a
flatten (C []) = (C [])
flatten (C (x:xs) ) = (C flatten x) ++ (C flatten xs)
flatten (B a) = (C [a])
From what I can tell the issue is that the ++ operator is expecting a list for both of its arguments and I'm trying to give it something of type A. I've added the A type so the function can either get a single element or a list of elements.
Does anyone know a different way to do this differently, or explain what I can do to fix the type error?
It's a bit unclear what you are asking for, but flattening a list of list is a standard function called concat in the prelude with type signature [[a]] -> [a].
If you make a data type of nested lists as you have started above, maybe you want to adjust your data type to something like this:
data Lists a = List [a] | ListOfLists [Lists a]
Then you can flatten these to a list;
flatten :: Lists a -> [a]
flatten (List xs) = xs
flatten (ListOfLists xss) = concatMap flatten xss
As a test,
> flatten (ListOfLists [List [1,2],List [3],ListOfLists [List [4],List[5]]])
[1,2,3,4,5]
Firstly, the A type is on the right track but I don't think it's quite correct. You want it to be able to flatten arbitrarily nested lists, so a value of type "A a" should be able to contain values of type "A a":
data A a = B a | C [A a]
Secondly, the type of the function should be slightly different. Instead of returning a value of type "A a", you probably want it to return just a list of a, since by definition the function is always returning a flat list. So the type signature is thus:
flatten :: A a -> [a]
Also note that no typeclass constraints are necessary -- this function is completely generic since it does not look at the list's elements at all.
Here's my implementation:
flatten (B a) = [a]
flatten (C []) = []
flatten (C (x:xs)) = flatten x ++ flatten (C xs)
this one liner will do the job. Although as it was mentioned by Malin the type signature is different:
flatten :: [[a]] -> [a]
flatten xs = (\z n -> foldr (\x y -> foldr z y x) n xs) (:) []
simple test
frege> li = [[3,4,2],[1,9,9],[5,8]]
frege> flatten li
[3,4,2,1,9,9,5,8]
Flatten via list comprehension.
flatten arr = [y | x<- arr, y <- x]