24

Can't figure out how to merge two lists in the following way in Haskell:

INPUT:  [1,2,3,4,5] [11,12,13,14]

OUTPUT: [1,11,2,12,3,13,4,14,5]
Will Ness
  • 62,652
  • 8
  • 86
  • 167
bogatyrjov
  • 5,077
  • 9
  • 29
  • 60
  • 7
    Usually you learn more if you explain what you tried and why it didn't work, that way people can do some filling-in-the-gaps instead of just giving you a chunk of code. – Thomas M. DuBuisson Oct 14 '10 at 23:56
  • Related: [Interleave list of lists in Haskell](http://stackoverflow.com/q/14186433/2157640) – Palec Apr 27 '15 at 21:13

6 Answers6

63

I want to propose a lazier version of merge:

merge [] ys = ys
merge (x:xs) ys = x:merge ys xs

For one example use case you can check a recent SO question about lazy generation of combinations.
The version in the accepted answer is unnecessarily strict in the second argument and that's what is improved here.

Community
  • 1
  • 1
Daniel
  • 24,697
  • 12
  • 57
  • 88
  • Well, that puts all of the elements of ys at the end, so it doesn't work. But I think what you meant was to reverse the order of the first two equations in andri's solution. – Yitz Oct 21 '10 at 17:44
  • 15
    No, it does the same thing - alternates between each list. Notice that `xs` and `ys` are swapped in the recursive call. – Daniel Oct 21 '10 at 18:25
  • 2
    It's a great solution! I wish I could think of something like that myself – bogatyrjov Oct 21 '10 at 23:01
  • 1
    Why is it that this version is lazy and something like ` merge (x:xs) (y:ys) = x:y: merge xs ys merge xs [] = xs merge [] ys = ys ` is not ? – Shitikanth Jul 30 '13 at 04:52
  • 3
    @Shitikanth did you look at the link from my answer? That's an example where you need the extra laziness of this version of merge. Your merge is lazy too but it forces the second argument unnecessarily through pattern matching. – Daniel Jul 30 '13 at 21:43
46
merge :: [a] -> [a] -> [a]
merge xs     []     = xs
merge []     ys     = ys
merge (x:xs) (y:ys) = x : y : merge xs ys
andri
  • 10,945
  • 2
  • 36
  • 49
  • 3
    I am new to functional programming, and the code gets me wonder this: Does the tail call optimization apply in that form of recursion too? – Le Curious Jul 21 '13 at 11:01
  • 2
    No, it doesn't. The tail call is (:), and it needs no optimization. – Ingo Jul 21 '13 at 12:09
  • 1
    There is a lazier version of this in [another answer](http://stackoverflow.com/a/3987188/2157640). It’s lazy in the second parameter. – Palec Apr 27 '15 at 21:11
  • @Ingo In regards to Le Curious question, does it not keep all of the elements on the stack until the base case is reached? Couldn't this overflow the stack? – Justin Meiners Apr 19 '19 at 15:33
  • @JustinMeiners it would if the (:) constructor would be strict in the second argument. But due to laziness, it won't evaluate the `merge xs ys` part until it is needed by the caller. But then, the call that created that list has already returned. – Ingo Apr 26 '19 at 05:30
25

So why do you think that simple (concat . transpose) "is not pretty enough"? I assume you've tried something like:

merge :: [[a]] -> [a]
merge = concat . transpose

merge2 :: [a] -> [a] -> [a]
merge2 l r = merge [l,r]

Thus you can avoid explicit recursion (vs the first answer) and still it's simpler than the second answer. So what are the drawbacks?

Ed'ka
  • 5,755
  • 20
  • 25
  • Ah, I forgot about transpose, and missed the comment. Very nice, +1 (But I wouldn't necessarily say that it's much easier than my first solution.) – danlei Oct 15 '10 at 13:53
  • 3
    Agree. Your solution is probably even more straightforward.. The real problem with it though is that it isn't 100% correct: for the lists of different lengths (like in the sample input from the question) it doesn't work as expected (tailing '5' is missing). – Ed'ka Oct 15 '10 at 14:03
  • Good catch! I overlooked the 5 in the sample output. I'll update my answer with a pointer to your answer and comments. Thanks! – danlei Oct 15 '10 at 15:50
  • Correct me if I'm wrong, but while the efficiency of the algorithm in the accepted answer is O(n), the "concat . transpose" method efficiency is O(n^2) ? Also I thought that it would be better to get through the problem without importing the additional Modules (List) ? – bogatyrjov Oct 15 '10 at 16:45
  • 2
    Looks like both are O(n) although explicit recursion is more than 2 times faster and space efficient against Data.List implementation (which is expected - the latter generates lots of intermediate lists) with "ghc -O2". However I suspect the difference would be less obvious should, say, 'stream-fusion' implementation of "transpose" and "concat" be used. – Ed'ka Oct 15 '10 at 18:16
  • 2
    The main drawback is that the average person looking at it will have to stare at it and think for a while to understand why it works, whereas the other solutions are immediately obvious. Your solution is very elegant though. – Yitz Oct 21 '10 at 17:48
6

EDIT: Take a look at Ed'ka's answer and comments!

Another possibility:

merge xs ys = concatMap (\(x,y) -> [x,y]) (zip xs ys)

Or, if you like Applicative:

merge xs ys = concat $ getZipList $ (\x y -> [x,y]) <$> ZipList xs <*> ZipList ys
danlei
  • 13,622
  • 5
  • 55
  • 81
4

Surely a case for an unfold:

interleave :: [a] -> [a] -> [a]
interleave = curry $ unfoldr g
  where
    g ([], [])   = Nothing
    g ([], (y:ys)) = Just (y, (ys, []))
    g (x:xs, ys) = Just (x, (ys, xs))
dfeuer
  • 44,398
  • 3
  • 56
  • 155
idontgetoutmuch
  • 1,613
  • 11
  • 17
  • Your original code didn't work; `interleave [] [1,2,3]` would give `[]`. I think it should work now. – dfeuer Feb 11 '15 at 17:37
  • another case for your [`unfoldr'`](http://stackoverflow.com/q/24519530/849891) apomorphism! (then it'll be equivalent to [this answer](http://stackoverflow.com/a/3987188/849891) above). – Will Ness Jul 30 '15 at 19:24
  • @dfeuer (the above comment) – Will Ness Jul 30 '15 at 22:50
-2
-- ++
pp [] [] = []
pp [] (h:t) = h:pp [] t
pp (h:t) [] = h:pp t []
pp (h:t) (a:b) = h : pp t (a:b)
Cody Gray
  • 222,280
  • 47
  • 466
  • 543