Haskell - alternating elements from two lists
Asked Answered
B

5

14

I'm trying to write a haskell function that takes in two lists of integers and generates a list with elements that have been taken alternatingly from the two lists.

I have the function:

blend xs ys

An example:

blend [1,2,3] [4,5,6]

should return

[1,4,2,5,3,6]

My logic is to zip the two lists together, generating the pairs of alternate elements, then somehow remove them from their tuples.

It's removing them from their tuples that I can't figure out how to implement.

Buber answered 12/12, 2011 at 6:20 Comment(0)
G
21

How about exchanging the arguments during recursion-descend?

blend (x:xs) ys = x:(blend ys xs)
blend _ _ = []

You can even generalise this approach for any number of lists (I'll leave this to you) or take the remaining elements of a list if the other is empty:

blend _ ys = ys
Gertrudgertruda answered 12/12, 2011 at 6:26 Comment(0)
E
8

If you want to zip, generate lists instead of tuples:

concat $ zipWith (\x y -> [x,y]) [1,2,3] [4,5,6]

Some pointless fun:

concat $ zipWith ((flip(:)).(:[])) [1,2,3] [4,5,6]  

Probably the easiest way:

import Data.List
concat $ transpose [[1,2,3],[4,5,6]]
Estevan answered 12/12, 2011 at 8:3 Comment(0)
T
7

I will assume that this is homework. Provided that you can create the following list (as you said):

[(1,4),(2,5),(3,6)]

... you can solve it with 2 functions:

  1. You need to convert a tuple (a, b) into a list [a, b]. Try using pattern matching! This function needs to be applied (aka. mapped) over all elements of the list you have.
  2. You will have a list of lists, like [[1,4],[2,5],[3,6]], so you need a function for concatenating the sublists into one big list.

There are of course other, maybe superior, ways to solve this problem, but it might be a good idea to continue with your original approach.

Trustbuster answered 12/12, 2011 at 6:31 Comment(1)
I think an answer to a [homework] question that continues on in the original direction is particularly helpful (unless the direction was completely wrong, which, in this case, it wasn't).Expiry
M
3

A solution without using concat or explicit recursion:

blend l = foldr($)[] . zipWith(.) (map(:)l) . map(:)

We can make also make this point-free

blend' = (foldr($)[].) . (.map(:)) . zipWith(.) . map(:)


How it works: first decorate both lists with cons operators
\[1,2,3] [4,5,6] -> [1:, 2:, 3:] [4:, 5:, 6:]

then we zip this together with function composition

-> [(1:).(4:), (2:).(5:), (3:).(6:)]

and finally fold the application of all these compositions from the right to the empty list

-> (1:).(4:) $ (2:).(5:) $ (3:).(6:) $ [] = 1:4:2:5:3:6:[] = [1,4,2,5,3,6]
Milstone answered 12/12, 2011 at 11:56 Comment(0)
D
1

Your blend function seems to be a limited version of flatZip. The flatZip function is similar but works for any number of lists of varying lengths. Using flatZip to implement blend will cause blend to also support varying lengths by default. Therefore, using a flatZip based approach may not be the way to go in situations where trimming the input lists to equal length is part of the desired behaviour.

The name flatZip refers to "a zipish way of flattening". Note the -ish part though. We can implement the function by composing concat with transpose. We can add blend on top of flatZip as syntactic sugar to verify that our implementation matches the desired behaviour.

import Data.List

flatZip = concat . transpose
flatZip([[1,2],[3],[4,5,6]]) --[1,3,4,2,5,6]

blend xs ys = flatZip [xs, ys]
blend [1,2,3] [4,5,6] --[1,4,2,5,3,6]
Dincolo answered 22/12, 2020 at 12:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.