3

I am trying to set up some functions to help with a current project I am working on. I am new to Haskell and struggling to implement my desired functions.

I have a list [a] and would like it to output a tuple of four different lists ([b],[b],[b],[b]) where each item in list [a] is successively placed in to the next list in the output tuple. So the first element in the input list [a] goes to the first list [b], the second element in [a] goes to the second list [b], the third element in [a] goes to the third list [b], and so on. I have tried using chunksOf and splitEvery/splitAt but cannot get the correct output. And help would be greatly appreciated! Thanks!

6
  • 1
    I think the output is then [a] -> ([a], [a], [a], [a])? So with [a]s as output, not [b]s. Can you share your implementation? Commented Mar 14, 2020 at 18:26
  • Yes, I was planning on altering the data type of each element of [a] as it is placed into its corresponding slot in [b] which is why I changed the names in this case. However, for this basic implementation, I think that it is correct to say that [a] will output since I still need to map my desired data type to each element in [a] as it is passed to the output tuple Commented Mar 14, 2020 at 19:36
  • A similar question was asked here: Haskell: Splitting list into tuple of two new lists That question is about two-tuples (twoples, I guess ;) ), but some of the answers (dave4220 and Yitz) can easily be expanded to your situation. Commented Mar 14, 2020 at 20:14
  • @MikaelF thanks for the link. this answer as well, with the foldr (\a ~(x,y) -> (a:y,x)) ([],[]) code snippet (which is also here and probably was known much earlier, too). to extend it to 3- or n-tuples... swapping is rotating, and the only thing left to decide is, in which direction? :) Commented Mar 14, 2020 at 21:18
  • (I've found this old answer of mine dealing with that, though in Scheme.) also, this question. Commented Mar 14, 2020 at 21:52

2 Answers 2

7

You each time "rotate" the 4-tuple and prepend to the first element. So we can implement this with a foldr pattern that looks like:

toFour :: [a] -> ([a], [a], [a], [a])
toFour = foldr (\a (bs, cs, ds, as) -> (a:as, bs, cs, ds)) ([], [], [], [])

or with an irrefutable pattern:

toFour :: [a] -> ([a], [a], [a], [a])
toFour = foldr (\a ~(bs, cs, ds, as) -> (a:as, bs, cs, ds)) ([], [], [], [])

So here (bs, cs, ds, as) is the 4-tuple that we generated for the tail of the list, and we "rotate" to the right to construct the tuple (as, bs, cs, ds) and then prepend the item a to the first list of the 4-tuple.

For a list of integers, this gives us:

Prelude> toFour [1..2]
([1],[2],[],[])
Prelude> toFour [1..3]
([1],[2],[3],[])
Prelude> toFour [1..4]
([1],[2],[3],[4])
Prelude> toFour [1..5]
([1,5],[2],[3],[4])
Prelude> toFour [1..6]
([1,5],[2,6],[3],[4])
Prelude> toFour [1..10]
([1,5,9],[2,6,10],[3,7],[4,8])

When we work with the irrefutable pattern, this is done lazily, so we can for example distribute elements of an infinite list, and then take for example obtain the first 10 elements of the second item:

Prelude> (\(_, l, _, _) -> take 10 l) (toFour [1..])
[2,6,10,14,18,22,26,30,34,38]
Sign up to request clarification or add additional context in comments.

5 Comments

This is a clever solution! I think it could also be generalized to arbitrary numbers of lists by using a list or sequence instead of a tuple.
You could use the standard two-list based queue, I think. haskell bucket i = uncurry (++) . fmap reverse . foldr f ([], replicate i []); where f x (xs,y:ys) = ((x:y):xs,ys); f x (xs,[]) = let y:ys = reverse xs in ([x:y],ys);
@ReinHenrichs this is far from being the first time this trick makes an appearance in an SO answer. I myself used it in an answer or two (or was it in comments?). Since I didn't come up with it myself, I always point out I first saw it in an F# (or was it ML?) answer by user "Ed'ka" -- I remember being quite blown away by it. His code was for a two-tuple of lists, swapping the tuple at each step; it was a nice exercise to come up with 3-tuples generalization, as I recall, :) to figure out the correct tuple rotation direction (which can then be used for any n, of course).
@ReinHenrichs this. (turns out I even gave that answer a bounty :) )
and it turns out it's in the haskellwiki since forever. :)
3

Define

f n = transpose . chunksOf n
g xs = let [xs1, xs2, xs3, xs4] = f 4 xs in (xs1, xs2, xs3, xs4) 

Where f is the general solution. This gives

Prelude> g ['a' .. 'z']
("aeimquy","bfjnrvz","cgkosw","dhlptx") 

Signatures

Prelude> :t g
g :: [a] -> ([a], [a], [a], [a])

and

Prelude> :t f
f :: Int -> [a] -> [[a]]

Info on:

1 Comment

map (take 5) . transpose . chunksOf 3 $ [1..] diverges for some reason. :) must slap a take 3 on top that transpose for the proper laziness.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.