Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell - if else return clean tuple

My homework task is to group two tuples in a list if the second element of the first tuple is the same as the first element of the second tuple. Then, if the first tuple is (a, b) and the second is (b, c), the tuple (a, c) must be added to the result list.

I wrote first function wich takes element with one tuple and second list with many tuples and compare each to each.

this one works correcly:

c1 = ("a","x")
d1 = [ ("x","b"), ("z","c"), ("x","b"), ("z","c")
     , ("x","b"), ("z","c"), ("x","b"), ("z","c") ]

getByOne c1 a1 = filter (/=[])
  [ if (fst  (last(take n a1))) == (snd c1)
    then [((fst c1),  (snd  (last(take n a1))))]
    else  [] | n <- [1..(length a1) ] ]

output:

[ [("a","b")], [("a","b")], [("a","b")], [("a","b")] ]

But the problems is that I can't throw in if then and else statement just simple tuple, so I create a new list. In the end of this "workaround" i am getting list in list in list and so on. Also if output list was bigger, there were be more lists in lists.

Is there any way to pass out only tuple or empty tuple or i should somehow group theses lists ?

like image 309
Brivvirs Avatar asked Nov 04 '12 09:11

Brivvirs


1 Answers

You can flatten the result using

concat :: [[a]] -> [a]

then you don't even need to filter (/=[]) - by the way, the condition (/= []) is more idiomatically written not . null, since the null test doesn't impose an Eq constraint on its argument (you already have one here, so it's just a matter of idiom).

Further, last (take n a1) is just the n-th element of a1 if 1 <= n <= length a1. Since you have that restriction imposed, that can more succinctly be expressed as a1 !! (n-1)

Then you have structurally

getByOne c1 a1 = concat $ [something c1 (a1 !! i) | i <- [0 .. length a1 - 1]]

(I have shifted indices to index by i), which is clearer and more efficiently expressed as

getByOne c1 a1 = concat $ map (something c1) a1

If you prefer a list comprehension to map, you can also write that as

getByOne c1 a1 = concat [something c1 x | x <- a1]

which in your case, using the ability to pattern-match in alist-comprehension generator, gives us

getByOne (f,s) a1 = concat [ if a == s then [(f,b)] else [] | (a,b) <- a1]

which is much shorter and more readable. Instead of using if condition then [element] else [] and concat, even nicer is to use a condition in the list comprehension,

getByOne (f,s) list = [(f,b) | (a,b) <- list, a == s]

which is short and clear.

like image 98
Daniel Fischer Avatar answered Oct 01 '22 02:10

Daniel Fischer