cheshirecatalyst
cheshirecatalyst

Reputation: 379

Consecutive n-tuples from a list in haskell

Is there an easy-ish / general way to get consecutive n-tuples? For example:

f3 [1,2,3,4,5,6] = [(1,2,3),(2,3,4),(3,4,5),(4,5,6)]
f4 [1,2,3,4,5,6] = [(1,2,3,4),(2,3,4,5),(3,4,5,6)]

Similar question however it only covers pairs (x,y).

Upvotes: 1

Views: 129

Answers (1)

K. A. Buhr
K. A. Buhr

Reputation: 50829

As @amalloy points out in the comments, there's no straightforward way to define this operation generally across different tuple sizes, but if you're just looking for a general pattern for writing a set of such functions, there are a few possibilities.

The simplest, though it looks a little physically repulsive, is just to write a simple recursive pattern match that spits out a tuple and then recurses on the tail of the argument, stopping as soon as the list becomes too short:

f3 :: [a] -> [(a,a,a)]
f3 (x1:nxt@(x2:x3:_)) = (x1,x2,x3):f3 nxt
f3 _ = []

f4 :: [a] -> [(a,a,a,a)]
f4 (x1:nxt@(x2:x3:x4:_)) = (x1,x2,x3,x4):f4 nxt
f4 _ = []

While you could generalize the solution you linked to for pairs by writing:

f3' = zip3 <*> tail <*> tail.tail
f4' = zip4 <*> tail <*> tail.tail <*> tail.tail.tail

this is too clever by half and also not a particularly efficient solution, as every argument repeats the tail operations of the previous argument when the results could be reused.

An alternative and probably reasonably approach is:

f3'' str = let l1:l2:l3:_ = tails str in zip3 l1 l2 l3
f4'' str = let l1:l2:l3:l4:_ = tails str in zip4 l1 l2 l3 l4

though it relies on having the right zipk available. While zip3 is defined in Prelude and zip4 through zip7 are defined in Data.List, if you need zip8 or more, you need to define it yourself.

Upvotes: 2

Related Questions