Reputation: 31
I wrote a function to merge two pair lists, in this case each element containing a character and a number.
mergeTupleList [] blist = blist
mergeTupleList alist [] = []
mergeTupleList (a:ab:as) (b:bn:bs) =
if ab == b
then a:[] ++ bn:[] ++ mergeTupleList (a:ab:as) (bs)
else [] ++ mergeTupleList (as) (b:bn:bs)
listA = [('a',1),('b',1),('a',2),('b',1)]
listZ = [('b',1),('z',1),('b',1),('z',2)]
It seems that in the 'else' case (b:bn:bs) is not the whole pair list loaded in the beginning, but the short version (bs) because it has been filtered by the 'then' condition. I'm looking for a way to transmit the original (b:bn:bs) list to this function.
input and output:
*Main> mergeTupleList ListA ListZ
[('a',1),('z',1),('a',1),('z',2)]
expected output:
[('a',1),('z',1),('a',1),('z',2),('a',2),('z',1),('a',2),('z',2)]
For clarification :
Perhaps merging is not the adequate word. For every (second) element in
ListA
existing in the originalListZ
write the element following it.
Another example:
ListA = [1,0,3,0]
ListZ = [0,8,0,9]
*Main> mergeTupleList listA listZ
[1,8,1,9]
expected output:
[1,8,1,9,3,8,3,9]
Upvotes: 0
Views: 105
Reputation: 31
This question has already been answered but here is what I figured out, with help from the comments.
First we create a groupByTwo
function to break listA
in lists containing each two elements.
groupByTwo [] = []
groupByTwo list =
(take 2 list) : (groupByTwo (drop 2 list))
Then we create a mergeTupleList'
function to run the simple mergeTupleList
on each element of the list of lists.
mergeTupleList' [] _ = []
mergeTupleList' (a:as) list2 =
mergeTupleList a list2 ++ mergeTupleList' as list2
Finally a third function called groupTupleMerge
to make life easier.
groupTupleMerge list1 list2 =
mergeTupleList' (groupByTwo list1) list2
Upvotes: 0
Reputation: 1600
I have a series of solutions for you. First, the following produces the desired output. I think this is as specified, but note that mergeTupleList'
no longer returns the bList in it's first line. That wasn't performing as expected, although your description leads me to believe that you do want that.
The secret to this solution is the inclusion of the subfunction tupleAB
, which does all the tupling for the first a:ab
, consuming the entire bs
list before evaluating the next ab
pair for the whole bs
list.
mergeTupleList' [] _ = []
mergeTupleList' alist [] = []
mergeTupleList' (a:ab:as) bbnbs =
let
tupleAB (b:bn:bs) =
if ab == b
then a:[] ++ bn:[] ++ tupleAB bs
else [] ++ tupleAB bs
tupleAB _ = []
in tupleAB bbnbs ++ mergeTupleList' as bbnbs
Next is a slight cleanup of all those naughty :[] ++
mergeTupleList'' [] _ = []
mergeTupleList'' alist [] = []
mergeTupleList'' (a:ab:as) bbnbs =
let
tupleAB (b:bn:bs) =
if ab == b
then a : bn : tupleAB bs
else tupleAB bs
tupleAB _ = []
in tupleAB bbnbs ++ mergeTupleList'' as bbnbs
The above solutions have some dangling case expressions. What do we do in the case where one of the lists isn't even numbered? If we instead turn each first two terms into a pair, we have lists of pairs (of pairs). This conversion function changes your lists.
pairListCvt :: [a] -> [(a,a)]
pairListCvt (a:b:cs) = (a,b) : pairListCvt cs
pairListCvt (a:[]) = [] --maybe should be error?
pairListCvt _ = []
listACvt = pairListCvt listA
listZCvt = pairListCvt listZ
Now, rewritten again, we have less unmatched patterns, and so probably less chance for error. This is an example of selecting a type to provide a bit of safety; type safety.
mergeTupleList''' [] _ = []
mergeTupleList''' alist [] = []
mergeTupleList''' ((a,ab):as) bbnbs =
let
tupleAB ((b,bn):bs) =
if ab == b
then (a , bn) : tupleAB bs
else mergeTupleList''' as ((b,bn):bs)
tupleAB _ = []
in tupleAB bbnbs ++ mergeTupleList''' as bbnbs
Next, since we are using each term in alist
to create terms from blist
,
I use a map
here.
mergeTupleList'''' as bbns =
let
tupleAB ((b,bn):bs) (a,ab) =
if ab == b
then (a, bn) : tupleAB bs (a,ab)
else tupleAB bs (a,ab)
tupleAB [] _ = []
in concat $ map (tupleAB bbns) as
Finally, the inner tupleAB
can be formulated as a fold
; something that incrementally builds a data structure through an element-by-element decomposition of the list. The core logic of that fold is now held in f
.
mergeTupleList''''' as bbns =
let
tupleAB bs aab = foldr (f aab) [] bs
f (a,ab) (b,bn) acc
| ab == b = (a,bn) : acc
| otherwise = acc
in concat $ map (tupleAB bbns) as
I prefer where bindings to let bindings, stylistically. Alghouth there are memory performance issues I don't understand concerning the choice.
mergeTupleListW as bbns = concat $ map (tupleAB bbns) as
where
tupleAB bs aab = foldr (f aab) [] bs
f (a,ab) (b,bn) acc
| ab == b = (a,bn) : acc
| otherwise = acc
Extra Credit. He is the function written as a List monad.
mergeTupleListM as bbns =
do a <- as
tupleAB bbns a
where
tupleAB bs aab = foldr (f aab) [] bs
f (a,ab) (b,bn) acc
| ab == b = (a,bn) : acc
| otherwise = acc
Upvotes: 1