Wednesday 7 November 2012

Reverse Interleave

Today's example is a reverse interleave - - that is, instead of taking two or more sequences and blending them together, we take one sequence and split it down into two or more by adding elements from the original sequence to the new sequences in rotation.

For example we want the function f that does this:-

(f '(1 2 3 4 5 6) 2) => ((1 3 5) (2 4 6)) ; split into two
(f '(1 2 3 4 5 6) 3) => ((1 4) (2 5) (3 6)) ; split into three

OK, so I expect I will want code to get every nth element from a sequence - every 2nd, every 3rd one etc. The built-in function partition-all splits a sequence into sequences of a nominated length: so all I want from this is the first element of every sub-sequence.  I map first onto the sequence of sequences returned by partition-all:-

user=> (def t '(1 2 3 4 5 6))
#'user/t
user=> t
(1 2 3 4 5 6)
user=> (partition-all 2 t)
((1 2) (3 4) (5 6))
user=> (partition-all 3 t)
((1 2 3) (4 5 6))
user=> (map first (partition-all 2 t))
(1 3 5)
user=> (map first (partition-all 3 t))
(1 4)


Then moving on I will want to get every nth element but not starting at the beginning, starting at some i-th element in.  So I have to drop some elements before the rest of the function gets it - something like this:-

defn every-nth-from-i
  "return every nth item from sequence s starting from element i (counting from zero)"
  [n s i]
  (map first (partition-all n (drop i s))))


user=> (every-nth-from-i 3 t 0)
(1 4)
user=> (every-nth-from-i 3 t 1)
(2 5)
user=> (every-nth-from-i 3 t 2)
(3 6)


Now the result I want is a sequence of all the sequences generated by this code, for each value of i up to the length of the sub-sequences I want to generate. Well, one less.  That's ok, given n I want to map the numbers in (range n) to the every-nth code.

(def reverse-interleave
  (fn [n s]
    (map
      (fn [i] (map first (partition-all n (drop i s))))
      (range 0 n))))


I don't need to explicitly pass n and s to the internal function because it executes in the environment where they are already available.

Does this work?

user=> (reverse-interleave 2 t)
((1 3 5) (2 4 6))
user=> (reverse-interleave 3 t)
((1 4) (2 5) (3 6))


Actually that turned out simpler than I expected.

No comments:

Post a Comment