rustiteratorrust-itertools

How to interlace an iterator with itself from the end?


I have an iterator in the form 0..=63, i.e.
0 1 2 3 4 ... 59 60 61 62 63.
Its .count() is 64.

How would I get the following iterator:
0 63 1 62 2 61 3 60 4 59 ...
(of course independent of the items present in the iterator), preferably without cloning?
The .count() should stay the same, as only the order of items should change.

I've looked in the standard library and couldn't find it, same in the itertools crate.


Solution

  • This solution works for all iterators that implement DoubleEndedIterator:

    Note that this solution is guaranteed to return all items, regardless of whether the iterator contains an even or odd number of them.

    fn selfinterlace<Iter>(mut iter: Iter) -> impl Iterator<Item = Iter::Item>
    where
        Iter: DoubleEndedIterator,
    {
        let mut from_front = false;
    
        std::iter::from_fn(move || {
            from_front = !from_front;
            if from_front {
                iter.next()
            } else {
                iter.next_back()
            }
        })
    }
    
    fn main() {
        let range = (0..=8).into_iter();
        let iter = selfinterlace(range);
        println!("{:?}", iter.collect::<Vec<_>>());
    }
    
    [0, 8, 1, 7, 2, 6, 3, 5, 4]
    

    The idea is that you store whether the next item should be from the front or the back, and then flip that in every iteration.

    from_fn can take FnMut, meaning, it can take closures that store internal state. The internal state in this closure consists of the variables iter and from_front, which get moved into the closure through the move || keyword.