How split list of tuples to tuple of lists? - list

I'm asked to split list of tuples to tuple that has 2 list, in the following way:
splitter ([{1,2},{3,4},{5,6}]). = {[1,3,5],[2,4,6]}
I've tried to write the following code:
splitter([]) -> {[],[]};
splitter(L) ->(map (fun ({A, _}) -> A end, L))|(map (fun ({_, B}) -> B end, L)).
And it does not compile, the error I am getting is: syntax error before: '|'
where is the problem?:

You are missing the outer curly braces. Also, replace the | by , as that's what's used to separate tuple members.
splitter([]) -> {[],[]};
splitter(L) -> {(map (fun ({A, _}) -> A end, L)),
(map (fun ({_, B}) -> B end, L))
}.

Empty list clause is unnecessary. You can use list comprehensions as well.
splitter(L) -> {[ X || {X, _} <- L], [X || {_, X} <- L]}.
You can make universal one as well
splitter_n(L) ->
list_to_tuple(transpose([tuple_to_list(X) || X <- L])).
transpose([]) -> [];
transpose([[]|_]) -> [];
transpose(L) ->
[[hd(X) || X <- L] | transpose([tl(X) || X <- L])].
In work
1> c(splitter).
{ok,splitter}
2> splitter:splitter_n([{1,2},{3,4},{5,6}]).
{[1,3,5],[2,4,6]}
3> splitter:splitter_n([{1,2,3},{4,5,6},{7,8,9}]).
{[1,4,7],[2,5,8],[3,6,9]}

Related

Reverse list of tuples of nodes and edges (Haskell)

I have a list of nodes and edges, represented as tuples where the first element is a node, and the second element is a list of all nodes it has an edge to. I am trying to reverse the list like so:
ghci> snuN [("a",["b"]),("b",["c"]),("c",["a","d"]),("e",["d"])]
ghci> [("a",["c"]),("b",["a"]),("c",["b"]),("d",["c","e"]),("e",[])]
So far, I've written this code:
snuH :: Eq t => [(t,[t])] -> [(t,[t])]
snuH [] = []
snuH ps#((x, xs):rest) =
if (length xs <= 1) && not (x `isInSublist` ps)
then [(y,[x])| y <- xs] ++ snuH rest ++ [(x, [])]
else [(y,[x])| y <- xs] ++ snuH rest
isInSublist :: Eq t => t -> [(t,[t])] -> Bool
isInSublist _ [] = False
isInSublist x ((y, ys):rest) = (x `elem` ys) || isInSublist x rest
combine :: Eq t => [(t,[t])] -> [(t,[t])]
combine ps#((x, xs):(y, ys):rest) = if x == y then (x, xs++ys):rest else (x, xs):combine((y, ys):rest)
snuN :: Eq t => [(t, [t])] -> [(t, [t])]
snuN ls = combine $ snuH ls
The first function gives me this output:
ghci> snuH [("a",["b"]),("b",["c"]),("c",["a","d"]),("e",["d"])]
ghci> [("b",["a"]),("c",["b"]),("a",["c"]),("d",["c"]),("d",["e"]),("e",[]),("b",[])]
Which is not quite the result I wanted, because it creates two tuples with the same first element (("d",["c"]),("d",["e"])), and it has the extra ("b",[]) as an element when it shouldn't. I wrote the combine helper-function to fix the problem, which gives me this output:
ghci> snuN [("a",["b"]),("b",["c"]),("c",["a","d"]),("e",["d"])]
ghci> [("b",["a"]),("c",["b"]),("a",["c"]),("d",["c","e"]),("e",[]),("b",[])]
Which fixes the problem with the two tuples with the same first element, but I still have the extra ("b",[]) which I can't figure out how to fix, I assume there's something wrong with my snuH but I can't see where the problem is.
Can you tell me what im doing wrong here? I don't understan why I get the extra ("b",[]). All help is appreciated!
I'd argue that the following list comprehension gives you what you need:
type Graph node = [(node, [node])]
converse :: Eq node => Graph node -> Graph node
converse g = [(v, [e | (e, es) <- g, v `elem` es]) | (v, _) <- g]
However, if you try it out, you'll get:
> converse [("a",["b"]),("b",["c"]),("c",["a","d"]),("e",["d"])]
[("a",["c"]),("b",["a"]),("c",["b"]),("e",[])]
Compared to the example you gave, the entry for "d" is missing from the output. That's because the input did not mention an explicit entry ("d", []).
To compensate for this, we could put a bit more logic in retrieving the complete list of nodes from the graph, also accounting for the "implied" ones:
nodes :: Eq node => Graph node -> [node]
nodes g = nub $ concat [v : es | (v, es) <- g]
Note: this requires importing nub from Data.List.
Then, we can write:
converse' :: Eq node => Graph node -> Graph node
converse' g = [(v, [e | (e, es) <- g, v `elem` es]) | v <- nodes g]
And, indeed, we yield:
> converse' [("a",["b"]),("b",["c"]),("c",["a","d"]),("e",["d"])]
[("a",["c"]),("b",["a"]),("c",["b"]),("d",["c","e"]),("e",[])]
You have [(a, [a])], which maps nodes to the nodes they have an edge to. One approach to "reversing" this is to first convert it to a list of all the edges. We can actually generalize the type a bit here, to distinguish from and to nodes.
allEdges :: [(a, [b])] -> [(a, b)]
allEdges g = [(a, b) | (a, bs) <- g, b <- bs]
Now it's just a matter of gathering up the nodes with an edge to each particular node:
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as M
gather :: Ord b => [(a,b)] -> Map b [a]
gather edges = M.fromListWith (++) [(b, [a]) | (a, b) <- edges]
Now we can just use M.assocs to convert that map to a list!
The above code will leave out nodes that have no edges going to them. We can patch that up with a bit of extra work.
reverseGraph :: Ord a => [(a, [a])] -> [(a, [a])]
reverseGraph = M.assocs . M.fromListWith (++) . gunk
where
gunk g = [q | (a, bs) <- g, q <- (a, []) : [(b, [a]) | b <- bs]]
The idea here is that when we see (a, bs), we insert the empty edge set (a, []) along with the nonempty ones (b, [a] for each b in bs.

Generic types on function don't correspond properly

I have experience with functional programming in general, but I'm new to F#, and I can't get this code to compile no matter what I try:
let group2<'T> (sq: seq<'T>) : seq<'T * 'T> =
Seq.fold (fun (p, l) b -> match p with
| None -> (Some b, l)
| Some v -> (None, (v, b) :: l)) (None, []) sq
I don't understand what this error message is trying to tell me, and I can't for the life of me figure out why it won't compile as-is;
main.fs(2,19): error FS0001: This expression was expected to have type
'seq<'T * 'T>'
but here has type
''a * 'b'
main.fs(4,65): error FS0001: This expression was expected to have type
'seq<'T * 'T>'
but here has type
''a * 'b'
anyone with more F# experience have some advice?
So if you update your code like this
let group2<'T> (sq: seq<'T>) : seq<'T * 'T> =
Seq.fold (fun (p ,l) b -> match p with
| None -> (Some b, l)
| Some v -> (None, (v, b) :: l)) (None, []) sq
|> snd
|> List.rev
|> Seq.ofList
It can work (by removing the state, and converting back from list to sequence). For example
group2 [1;2;3;4]
yields
[(1, 2); (3, 4)]
It's not very idiomatic as it mixes sequences and lists.
A more idiomatic code only for (even) lists:
let rec group2 (xs:'T list) =
match xs with
| [] -> []
| x::y::xs -> ( x, y)::group2 xs
| _ -> failwith "not even"
Basically you deal with 3 choices,
The list is empty, there are no pairs you return an empty list.
There are two items at the start, you pair them in a tuple and process the rest of the list recursively
There's only one item left, we fail because it's not posible to create a tuple with nothing*
If you want to consider odd lists, you can use option types: e.g. None/Some
let rec group2 (xs:'T list) =
match xs with
| [] -> []
| [x] -> [Some x, None]
| x::y::xs -> (Some x,Some y)::group2 xs
Finally you could use the chunkBySize library function for either (even) lists or sequences:
[1;2;3;4]
|> Seq.chunkBySize 2
|> Seq.map (fun a -> a.[0], a.[1])
or
[1;2;3;4]
|> List.chunkBySize 2
|> List.map (fun a -> a.[0], a.[1])

How to display the list but without a certain item.

I have the following list, that contains a list of strings..
I have already searched the orginal list for the lists that contain the string "tom" and got the following list
[["leo", "tom"], ["meg", "tom"], ["George", "john", "adam", "tom"] ]
I now wish to display this list without "tom", i would do this through list comprehension but i don't know how to do that for a list that contains lists? Can someone help me into the right direction?
Writing this as a list comprehension would get complicated, I think. Easier to just chain simple functions.
-- Intended as l `without` x
without :: Eq a => [a] -> a -> [a]
without l x = filter (/= x) l
containing :: Eq a => [[a]] -> a -> [[a]]
containing l x = filter (x `elem`) l
listsWithTom = lists `containing` "tom"
listsMinusTom = map (`without` "tom") listsWithTom
notom xss = [[ x | x <- xs, x /= "tom"] | xs <- xss]
Or
notom = map (filter (/= "tom"))
Or in your particular case
notom = map init

groupBy with multiple test functions

Is there a better and more concise way to write the following code in Haskell? I've tried using if..else but that is getting less readable than the following. I want to avoid traversing the xs list (which is huge!) 8 times to just separate the elements into 8 groups. groupBy from Data.List takes only one test condition function: (a -> a -> Bool) -> [a] -> [[a]].
x1 = filter (check condition1) xs
x2 = filter (check condition2) xs
x3 = filter (check condition3) xs
x4 = filter (check condition4) xs
x5 = filter (check condition5) xs
x6 = filter (check condition6) xs
x7 = filter (check condition7) xs
x8 = filter (check condition8) xs
results = [x1,x2,x3,x4,x5,x6,x7,x8]
This only traverses the list once:
import Data.Functor
import Control.Monad
filterN :: [a -> Bool] -> [a] -> [[a]]
filterN ps =
map catMaybes . transpose .
map (\x -> map (\p -> x <$ guard (p x)) ps)
For each element of the list, the map produces a list of Maybes, each Maybe corresponding to one of the predicates; it is Nothing if the element does not satisfy the predicate, or Just x if it does satisfy the predicate. Then, the transpose shuffles all these lists so that the list is organised by predicate, rather than by element, and the map catMaybes discards the entries for elements that did not satisfy a predicate.
Some explanation: x <$ m is fmap (const x) m, and for Maybe, guard b is if b then Just () else Nothing, so x <$ guard b is if b then Just x else Nothing.
The map could also be written as map (\x -> [x <$ guard (p x) | p <- ps]).
If you insist on one traversing the list only once, you can write
filterMulti :: [a -> Bool] -> [a] -> [[a]]
filterMulti fs xs = go (reverse xs) (repeat []) where
go [] acc = acc
go (y:ys) acc = go ys $ zipWith (\f a -> if f y then y:a else a) fs acc
map (\ cond -> filter (check cond) xs) [condition1, condition2, ..., condition8]
I think you could use groupWith from GHC.Exts.
If you write the a -> b function to assign every element in xs its 'class', I belive groupWith would split xs just the way you want it to, traversing the list just once.
groupBy doesn't really do what you're wanting; even if it did accept multiple predicate functions, it doesn't do any filtering on the list. It just groups together contiguous runs of list elements that satisfy some condition. Even if your filter conditions, when combined, cover all of the elements in the supplied list, this is still a different operation. For instance, groupBy won't modify the order of the list elements, nor will it have the possibility of including a given element more than once in the result, while your operation can do both of those things.
This function will do what you're looking for:
import Control.Applicative
filterMulti :: [a -> Bool] -> [a] -> [[a]]
filterMulti ps as = filter <$> ps <*> pure as
As an example:
> filterMulti [(<2), (>=5)] [2, 5, 1, -2, 5, 1, 7, 3, -20, 76, 8]
[[1, -2, 1, -20], [5, 5, 7, 76, 8]]
As an addendum to nietaki's answer (this should be a comment but it's too long, so if his answer is correct, accept his!), the function a -> b could be written as a series of nested if ... then .. else, but that is not very idiomatic Haskell and not very extensible. This might be slightly better:
import Data.List (elemIndex)
import GHC.Exts (groupWith)
f xs = groupWith test xs
where test x = elemIndex . map ($ x) $ [condition1, ..., condition8]
It categorises each element by the first condition_ it satisfies (and puts those that don't satisfy any into their own category).
(The documentation for elemIndex is here.)
The first function will return a list of "uppdated" lists and the second function will go through the whole list and for each value uppdate the list
myfilter :: a -> [a -> Bool] -> [[a]] -> [[a]]
myfilter _ [] [] = []
myfilter x f:fs l:ls | f x = (x:l): Myfilter x fs ls
| otherwise = l:Myfilter x fs ls
filterall :: [a] -> [a -> Bool] -> [[a]] -> [[a]]
filterall [] _ l = l
filterall x:xs fl l:ls = filterall xs fl (myfilter x fl l)
This should be called with filterall xs [condition1,condition2...] [[],[]...]

Help on a list manipulating list

Hi I am a newbie in Haskell.
I am trying to do a simple task.
test :: (RealFloat a) => a -> a -> [a]
test xs ys= [ w : h: [] | w <- xs, h <- ys]
I am getting an error here. (with out a doubt)
In this task, I am simply trying to bind two lists (ex: test [12.12] [14.14])
and hopefully return a new combined list (ex: [12.12,14.14])
thanks for your help
Your signature is wrong. Try:
test xs ys = ...
then in ghci:
> :t test
test :: [t] -> [t] -> [[t]]
You need two arguments, both are lists, not two arguments of single elements.
Drakosha is correct. List concatenation already has an operator in Haskell.
test :: (RealFloat a) => [a] -> [a] -> [a]
test xs ys= xs ++ ys
You probably don't want to use a list comprehension here, unless you want to extract every element in your first and second list and do something with them. For example, a Cartesian Product:
list1 = [1.0,1.1,1.2] :: [Double]
list2 = [2.0,2.1,2.2] :: [Double]
testComps xs ys = [(x,y) | x <- xs, y <- ys]
Or addition:
testComps2 xs ys = [ x + y | x <- xs, y <- ys]
Or even creating lists:
testComps3 xs ys = [x : y : [] | x <- xs, y <- ys]
In GHCi, this will yield the following:
*Main> testComps list1 list2
[(1.0,2.0),(1.0,2.1),(1.0,2.2),(1.1,2.0),(1.1,2.1),(1.1,2.2),(1.2,2.0),(1.2,2.1)
,(1.2,2.2)]
*Main> testComps2 list1 list2
[3.0,3.1,3.2,3.1,3.2,3.3000000000000003,3.2,3.3,3.4000000000000004]
*Main> testComps3 list1 list2
[[1.0,2.0],[1.0,2.1],[1.0,2.2],[1.1,2.0],[1.1,2.1],[1.1,2.2],[1.2,2.0],[1.2,2.1]
,[1.2,2.2]]
The weird results in testComps2 is, of course, normal cruft when you're dealing with floating-point numbers. In the real world you'd compensate for this by rounding.
Another problem you'll run into is the difference between (++) and (:). Simply put, (:) tacks individual items onto a list, whereas (++) concatenates two lists.
You need list concatenation:
[12.12] ++ [14.14]
=> [12.12,14.14]