From 79bdcf03c82a1d86cd09cfe4df1c83459e5c910e Mon Sep 17 00:00:00 2001 From: Ed Bassett Date: Sat, 15 Jun 2019 14:00:57 +1000 Subject: [PATCH] Add circular_tuple_windows --- src/lib.rs | 36 +++++++++++++++++++++++++++++++++++- src/tuple_impl.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index aa01a4f91..722b0c872 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -124,7 +124,7 @@ pub mod structs { pub use sources::{RepeatCall, Unfold, Iterate}; #[cfg(feature = "use_std")] pub use tee::Tee; - pub use tuple_impl::{TupleBuffer, TupleWindows, Tuples}; + pub use tuple_impl::{TupleBuffer, TupleWindows, CircularTupleWindows, Tuples}; #[cfg(feature = "use_std")] pub use unique_impl::{Unique, UniqueBy}; pub use with_position::WithPosition; @@ -582,6 +582,40 @@ pub trait Itertools : Iterator { tuple_impl::tuple_windows(self) } + /// Return an iterator over all windows, wrapping back to the first + /// elements when the window would otherwise exceed the length of the + /// iterator, producing tuples of a specific size (up to 4). + /// + /// `circular_tuple_windows` clones the iterator elements so that they can be + /// part of successive windows, this makes it most suited for iterators + /// of references and other values that are cheap to copy. + /// + /// ``` + /// use itertools::Itertools; + /// let mut v = Vec::new(); + /// for (a, b) in (1..5).circular_tuple_windows() { + /// v.push((a, b)); + /// } + /// assert_eq!(v, vec![(1, 2), (2, 3), (3, 4), (4, 1)]); + /// + /// let mut it = (1..5).circular_tuple_windows(); + /// assert_eq!(Some((1, 2, 3)), it.next()); + /// assert_eq!(Some((2, 3, 4)), it.next()); + /// assert_eq!(Some((3, 4, 1)), it.next()); + /// assert_eq!(Some((4, 1, 2)), it.next()); + /// assert_eq!(None, it.next()); + /// + /// // this requires a type hint + /// let it = (1..5).circular_tuple_windows::<(_, _, _)>(); + /// itertools::assert_equal(it, vec![(1, 2, 3), (2, 3, 4), (3, 4, 1), (4, 1, 2)]); + /// ``` + fn circular_tuple_windows(self) -> CircularTupleWindows + where Self: Sized + Clone + Iterator + ExactSizeIterator, + T: tuple_impl::TupleCollect + Clone, + T::Item: Clone + { + tuple_impl::circular_tuple_windows(self) + } /// Return an iterator that groups the items in tuples of a specific size /// (up to 4). /// diff --git a/src/tuple_impl.rs b/src/tuple_impl.rs index 0daa7800c..5b3049c95 100644 --- a/src/tuple_impl.rs +++ b/src/tuple_impl.rs @@ -171,6 +171,48 @@ impl Iterator for TupleWindows } } +/// An iterator over all windows,wrapping back to the first elements when the +/// window would otherwise exceed the length of the iterator, producing tuples +/// of a specific size. +/// +/// See [`.circular_tuple_windows()`](../trait.Itertools.html#method.circular_tuple_windows) for more +/// information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +#[derive(Debug)] +pub struct CircularTupleWindows + where I: Iterator + Clone, + T: TupleCollect + Clone +{ + iter: std::iter::Take, T>>, + phantom_data: std::marker::PhantomData +} + +pub fn circular_tuple_windows(iter: I) -> CircularTupleWindows + where I: Iterator + Clone + ExactSizeIterator, + T: TupleCollect + Clone, + T::Item: Clone +{ + let len = iter.len(); + let iter = tuple_windows(iter.cycle()).take(len); + + CircularTupleWindows { + iter: iter, + phantom_data: std::marker::PhantomData{} + } +} + +impl Iterator for CircularTupleWindows + where I: Iterator + Clone, + T: TupleCollect + Clone, + T::Item: Clone +{ + type Item = T; + + fn next(&mut self) -> Option { + self.iter.next() + } +} + pub trait TupleCollect: Sized { type Item; type Buffer: Default + AsRef<[Option]> + AsMut<[Option]>;