Skip to content

Commit 88796ed

Browse files
Merge #350
350: Add circular_tuple_windows r=jswrenn a=ed-bassett As described in #349, I think `circular_tuple_windows` would be broadly useful enough to be added to Itertools. It relies on `tuple_windows` and cloning, so I'm sure it could not be included in the standard library. I'm not sure if this is the best way to implement the function, but I thought I would give it a shot so that at the very least you'd have something functional to work from. Co-authored-by: Ed Bassett <ed@devsketchpad.com>
2 parents cae2ab2 + e605002 commit 88796ed

File tree

2 files changed

+80
-1
lines changed

2 files changed

+80
-1
lines changed

src/lib.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ pub mod structs {
127127
pub use crate::sources::{RepeatCall, Unfold, Iterate};
128128
#[cfg(feature = "use_std")]
129129
pub use crate::tee::Tee;
130-
pub use crate::tuple_impl::{TupleBuffer, TupleWindows, Tuples};
130+
pub use crate::tuple_impl::{TupleBuffer, TupleWindows, CircularTupleWindows, Tuples};
131131
#[cfg(feature = "use_std")]
132132
pub use crate::unique_impl::{Unique, UniqueBy};
133133
pub use crate::with_position::WithPosition;
@@ -584,6 +584,40 @@ pub trait Itertools : Iterator {
584584
tuple_impl::tuple_windows(self)
585585
}
586586

587+
/// Return an iterator over all windows, wrapping back to the first
588+
/// elements when the window would otherwise exceed the length of the
589+
/// iterator, producing tuples of a specific size (up to 4).
590+
///
591+
/// `circular_tuple_windows` clones the iterator elements so that they can be
592+
/// part of successive windows, this makes it most suited for iterators
593+
/// of references and other values that are cheap to copy.
594+
///
595+
/// ```
596+
/// use itertools::Itertools;
597+
/// let mut v = Vec::new();
598+
/// for (a, b) in (1..5).circular_tuple_windows() {
599+
/// v.push((a, b));
600+
/// }
601+
/// assert_eq!(v, vec![(1, 2), (2, 3), (3, 4), (4, 1)]);
602+
///
603+
/// let mut it = (1..5).circular_tuple_windows();
604+
/// assert_eq!(Some((1, 2, 3)), it.next());
605+
/// assert_eq!(Some((2, 3, 4)), it.next());
606+
/// assert_eq!(Some((3, 4, 1)), it.next());
607+
/// assert_eq!(Some((4, 1, 2)), it.next());
608+
/// assert_eq!(None, it.next());
609+
///
610+
/// // this requires a type hint
611+
/// let it = (1..5).circular_tuple_windows::<(_, _, _)>();
612+
/// itertools::assert_equal(it, vec![(1, 2, 3), (2, 3, 4), (3, 4, 1), (4, 1, 2)]);
613+
/// ```
614+
fn circular_tuple_windows<T>(self) -> CircularTupleWindows<Self, T>
615+
where Self: Sized + Clone + Iterator<Item = T::Item> + ExactSizeIterator,
616+
T: tuple_impl::TupleCollect + Clone,
617+
T::Item: Clone
618+
{
619+
tuple_impl::circular_tuple_windows(self)
620+
}
587621
/// Return an iterator that groups the items in tuples of a specific size
588622
/// (up to 4).
589623
///

src/tuple_impl.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
//! Some iterator that produces tuples
22
33
use std::iter::Fuse;
4+
use std::iter::Take;
5+
use std::iter::Cycle;
6+
use std::marker::PhantomData;
47

58
// `HomogeneousTuple` is a public facade for `TupleCollect`, allowing
69
// tuple-related methods to be used by clients in generic contexts, while
@@ -184,6 +187,48 @@ impl<I, T> Iterator for TupleWindows<I, T>
184187
}
185188
}
186189

190+
/// An iterator over all windows,wrapping back to the first elements when the
191+
/// window would otherwise exceed the length of the iterator, producing tuples
192+
/// of a specific size.
193+
///
194+
/// See [`.circular_tuple_windows()`](../trait.Itertools.html#method.circular_tuple_windows) for more
195+
/// information.
196+
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
197+
#[derive(Debug)]
198+
pub struct CircularTupleWindows<I, T: Clone>
199+
where I: Iterator<Item = T::Item> + Clone,
200+
T: TupleCollect + Clone
201+
{
202+
iter: Take<TupleWindows<Cycle<I>, T>>,
203+
phantom_data: PhantomData<T>
204+
}
205+
206+
pub fn circular_tuple_windows<I, T>(iter: I) -> CircularTupleWindows<I, T>
207+
where I: Iterator<Item = T::Item> + Clone + ExactSizeIterator,
208+
T: TupleCollect + Clone,
209+
T::Item: Clone
210+
{
211+
let len = iter.len();
212+
let iter = tuple_windows(iter.cycle()).take(len);
213+
214+
CircularTupleWindows {
215+
iter: iter,
216+
phantom_data: PhantomData{}
217+
}
218+
}
219+
220+
impl<I, T> Iterator for CircularTupleWindows<I, T>
221+
where I: Iterator<Item = T::Item> + Clone,
222+
T: TupleCollect + Clone,
223+
T::Item: Clone
224+
{
225+
type Item = T;
226+
227+
fn next(&mut self) -> Option<T> {
228+
self.iter.next()
229+
}
230+
}
231+
187232
pub trait TupleCollect: Sized {
188233
type Item;
189234
type Buffer: Default + AsRef<[Option<Self::Item>]> + AsMut<[Option<Self::Item>]>;

0 commit comments

Comments
 (0)