diff --git a/src/intersperse.rs b/src/intersperse.rs index 057929927..4cd2fa5a8 100644 --- a/src/intersperse.rs +++ b/src/intersperse.rs @@ -1,7 +1,6 @@ use std::iter::Fuse; use super::size_hint; -#[derive(Clone)] /// An iterator adaptor to insert a particular value /// between each element of the adapted iterator. /// @@ -11,7 +10,7 @@ use super::size_hint; /// /// See [`.intersperse()`](../trait.Itertools.html#method.intersperse) for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct Intersperse where I: Iterator { @@ -62,7 +61,7 @@ impl Iterator for Intersperse Self: Sized, F: FnMut(B, Self::Item) -> B, { let mut accum = init; - + if let Some(x) = self.peek.take() { accum = f(accum, x); } @@ -77,3 +76,81 @@ impl Iterator for Intersperse }) } } + +/// An iterator adaptor to insert a particular value created by a function +/// between each element of the adapted iterator. +/// +/// Iterator element type is `I::Item` +/// +/// This iterator is *fused*. +/// +/// See [`.intersperse_with()`](../trait.Itertools.html#method.intersperse_with) for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +#[derive(Clone, Debug)] +pub struct IntersperseWith + where I: Iterator, + ElemF: FnMut() -> I::Item, +{ + element: ElemF, + iter: Fuse, + peek: Option, +} + +/// Create a new IntersperseWith iterator +pub fn intersperse_with(iter: I, elt: ElemF) -> IntersperseWith + where I: Iterator, + ElemF: FnMut() -> I::Item +{ + let mut iter = iter.fuse(); + IntersperseWith { + peek: iter.next(), + iter: iter, + element: elt, + } +} + +impl Iterator for IntersperseWith + where I: Iterator, + ElemF: FnMut() -> I::Item +{ + type Item = I::Item; + #[inline] + fn next(&mut self) -> Option { + if self.peek.is_some() { + self.peek.take() + } else { + self.peek = self.iter.next(); + if self.peek.is_some() { + Some((self.element)()) + } else { + None + } + } + } + + fn size_hint(&self) -> (usize, Option) { + // 2 * SH + { 1 or 0 } + let has_peek = self.peek.is_some() as usize; + let sh = self.iter.size_hint(); + size_hint::add_scalar(size_hint::add(sh, sh), has_peek) + } + + fn fold(mut self, init: B, mut f: F) -> B where + Self: Sized, F: FnMut(B, Self::Item) -> B, + { + let mut accum = init; + + if let Some(x) = self.peek.take() { + accum = f(accum, x); + } + + let element = &mut self.element; + + self.iter.fold(accum, + |accum, x| { + let accum = f(accum, (element)()); + let accum = f(accum, x); + accum + }) + } +} diff --git a/src/lib.rs b/src/lib.rs index 0ab699e25..3dc0e5649 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -107,7 +107,7 @@ pub mod structs { pub use crate::format::{Format, FormatWith}; #[cfg(feature = "use_std")] pub use crate::groupbylazy::{IntoChunks, Chunk, Chunks, GroupBy, Group, Groups}; - pub use crate::intersperse::Intersperse; + pub use crate::intersperse::{Intersperse, IntersperseWith}; #[cfg(feature = "use_std")] pub use crate::kmerge_impl::{KMerge, KMergeBy}; pub use crate::merge_join::MergeJoinBy; @@ -390,6 +390,27 @@ pub trait Itertools : Iterator { intersperse::intersperse(self, element) } + /// An iterator adaptor to insert a particular value created by a function + /// between each element of the adapted iterator. + /// + /// Iterator element type is `Self::Item`. + /// + /// This iterator is *fused*. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let mut i = 10; + /// itertools::assert_equal((0..3).intersperse_with(|| { i -= 1; i }), vec![0, 9, 1, 8, 2]); + /// assert_eq!(i, 8); + /// ``` + fn intersperse_with(self, element: F) -> IntersperseWith + where Self: Sized, + F: FnMut() -> Self::Item + { + intersperse::intersperse_with(self, element) + } + /// Create an iterator which iterates over both this and the specified /// iterator simultaneously, yielding pairs of two optional elements. ///