Skip to content

Commit

Permalink
Merge #350
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
bors[bot] and ed-bassett committed Mar 23, 2020
2 parents cae2ab2 + e605002 commit 88796ed
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 1 deletion.
36 changes: 35 additions & 1 deletion src/lib.rs
Expand Up @@ -127,7 +127,7 @@ pub mod structs {
pub use crate::sources::{RepeatCall, Unfold, Iterate};
#[cfg(feature = "use_std")]
pub use crate::tee::Tee;
pub use crate::tuple_impl::{TupleBuffer, TupleWindows, Tuples};
pub use crate::tuple_impl::{TupleBuffer, TupleWindows, CircularTupleWindows, Tuples};
#[cfg(feature = "use_std")]
pub use crate::unique_impl::{Unique, UniqueBy};
pub use crate::with_position::WithPosition;
Expand Down Expand Up @@ -584,6 +584,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<T>(self) -> CircularTupleWindows<Self, T>
where Self: Sized + Clone + Iterator<Item = T::Item> + 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).
///
Expand Down
45 changes: 45 additions & 0 deletions src/tuple_impl.rs
@@ -1,6 +1,9 @@
//! Some iterator that produces tuples

use std::iter::Fuse;
use std::iter::Take;
use std::iter::Cycle;
use std::marker::PhantomData;

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

/// 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<I, T: Clone>
where I: Iterator<Item = T::Item> + Clone,
T: TupleCollect + Clone
{
iter: Take<TupleWindows<Cycle<I>, T>>,
phantom_data: PhantomData<T>
}

pub fn circular_tuple_windows<I, T>(iter: I) -> CircularTupleWindows<I, T>
where I: Iterator<Item = T::Item> + 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: PhantomData{}
}
}

impl<I, T> Iterator for CircularTupleWindows<I, T>
where I: Iterator<Item = T::Item> + Clone,
T: TupleCollect + Clone,
T::Item: Clone
{
type Item = T;

fn next(&mut self) -> Option<T> {
self.iter.next()
}
}

pub trait TupleCollect: Sized {
type Item;
type Buffer: Default + AsRef<[Option<Self::Item>]> + AsMut<[Option<Self::Item>]>;
Expand Down

0 comments on commit 88796ed

Please sign in to comment.