From 756ba95edddcce9dfb6c59caca5e7743c4af0d01 Mon Sep 17 00:00:00 2001 From: Owen Leung Date: Thu, 16 May 2024 23:20:44 +0800 Subject: [PATCH 1/7] Add multipeek_general.rs --- src/lib.rs | 1 + src/multipeek_general.rs | 251 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 252 insertions(+) create mode 100644 src/multipeek_general.rs diff --git a/src/lib.rs b/src/lib.rs index f4de79c50..aa4ffb086 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -204,6 +204,7 @@ mod kmerge_impl; mod lazy_buffer; mod merge_join; mod minmax; +mod multipeek_general; #[cfg(feature = "use_alloc")] mod multipeek_impl; mod pad_tail; diff --git a/src/multipeek_general.rs b/src/multipeek_general.rs new file mode 100644 index 000000000..568b7fafe --- /dev/null +++ b/src/multipeek_general.rs @@ -0,0 +1,251 @@ +#![allow(private_interfaces)] + +use crate::{size_hint, PeekingNext}; +use alloc::collections::VecDeque; +use std::iter::Fuse; + +/// See [`multipeek()`] for more information. +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +#[derive(Debug, Clone)] +struct MultiPeekGeneral { + pub iter: Fuse, + pub buf: VecDeque, + pub index: Idx, +} + +pub type MultiPeek = MultiPeekGeneral; +pub type PeekNth = MultiPeekGeneral; + +/// An iterator adaptor that allows the user to peek at multiple `.next()` +/// values without advancing the base iterator. +/// +/// [`IntoIterator`] enabled version of [`Itertools::multipeek`]. +pub fn multipeek(iterable: I) -> MultiPeek +where + I: IntoIterator, +{ + MultiPeek { + iter: iterable.into_iter().fuse(), + buf: VecDeque::new(), + index: 0, + } +} + +/// A drop-in replacement for [`std::iter::Peekable`] which adds a `peek_nth` +/// method allowing the user to `peek` at a value several iterations forward +/// without advancing the base iterator. +/// +/// This differs from `multipeek` in that subsequent calls to `peek` or +/// `peek_nth` will always return the same value until `next` is called +/// (making `reset_peek` unnecessary). +pub fn peek_nth(iterable: I) -> PeekNth +where + I: IntoIterator, +{ + PeekNth { + iter: iterable.into_iter().fuse(), + buf: VecDeque::new(), + index: (), + } +} + +trait PeekIndex { + fn reset_index(&mut self); +} + +impl PeekIndex for () { + fn reset_index(&mut self) {} +} + +impl PeekIndex for usize { + fn reset_index(&mut self) { + *self = 0; + } +} + +impl MultiPeekGeneral { + /// Works exactly like the `peek_mut` method in [`std::iter::Peekable`]. + pub fn peek_mut(&mut self) -> Option<&mut I::Item> { + self.peek_nth_mut(0) + } + + /// Returns a reference to the `nth` value without advancing the iterator. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use itertools::peek_nth; + /// + /// let xs = vec![1, 2, 3]; + /// let mut iter = peek_nth(xs.into_iter()); + /// + /// assert_eq!(iter.peek_nth(0), Some(&1)); + /// assert_eq!(iter.next(), Some(1)); + /// + /// // The iterator does not advance even if we call `peek_nth` multiple times + /// assert_eq!(iter.peek_nth(0), Some(&2)); + /// assert_eq!(iter.peek_nth(1), Some(&3)); + /// assert_eq!(iter.next(), Some(2)); + /// + /// // Calling `peek_nth` past the end of the iterator will return `None` + /// assert_eq!(iter.peek_nth(1), None); + /// ``` + pub fn peek_nth(&mut self, n: usize) -> Option<&I::Item> { + let unbuffered_items = (n + 1).saturating_sub(self.buf.len()); + + self.buf.extend(self.iter.by_ref().take(unbuffered_items)); + + self.buf.get(n) + } + + /// Returns a mutable reference to the `nth` value without advancing the iterator. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use itertools::peek_nth; + /// + /// let xs = vec![1, 2, 3, 4, 5]; + /// let mut iter = peek_nth(xs.into_iter()); + /// + /// assert_eq!(iter.peek_nth_mut(0), Some(&mut 1)); + /// assert_eq!(iter.next(), Some(1)); + /// + /// // The iterator does not advance even if we call `peek_nth_mut` multiple times + /// assert_eq!(iter.peek_nth_mut(0), Some(&mut 2)); + /// assert_eq!(iter.peek_nth_mut(1), Some(&mut 3)); + /// assert_eq!(iter.next(), Some(2)); + /// + /// // Peek into the iterator and set the value behind the mutable reference. + /// if let Some(p) = iter.peek_nth_mut(1) { + /// assert_eq!(*p, 4); + /// *p = 9; + /// } + /// + /// // The value we put in reappears as the iterator continues. + /// assert_eq!(iter.next(), Some(3)); + /// assert_eq!(iter.next(), Some(9)); + /// + /// // Calling `peek_nth_mut` past the end of the iterator will return `None` + /// assert_eq!(iter.peek_nth_mut(1), None); + /// ``` + pub fn peek_nth_mut(&mut self, n: usize) -> Option<&mut I::Item> { + let unbuffered_items = (n + 1).saturating_sub(self.buf.len()); + + self.buf.extend(self.iter.by_ref().take(unbuffered_items)); + + self.buf.get_mut(n) + } + + /// Works exactly like the `next_if` method in [`std::iter::Peekable`]. + pub fn next_if(&mut self, func: impl FnOnce(&I::Item) -> bool) -> Option { + match self.next() { + Some(item) if func(&item) => Some(item), + Some(item) => { + self.buf.push_front(item); + None + } + _ => None, + } + } + + /// Works exactly like the `next_if_eq` method in [`std::iter::Peekable`]. + pub fn next_if_eq(&mut self, expected: &T) -> Option + where + T: ?Sized, + I::Item: PartialEq, + { + self.next_if(|next| next == expected) + } + + /// Works exactly like `next_if`, but for the `nth` value without advancing the iterator. + pub fn nth_if(&mut self, n: usize, func: impl FnOnce(&I::Item) -> bool) -> Option<&I::Item> { + match self.peek_nth(n) { + Some(item) if func(item) => Some(item), + _ => None, + } + } + + /// Works exactly like `next_if_eq`, but for the `nth` value without advancing the iterator. + pub fn nth_if_eq(&mut self, n: usize, expected: &T) -> Option<&I::Item> + where + T: ?Sized, + I::Item: PartialEq, + { + self.nth_if(n, |item| item == expected) + } +} +impl MultiPeek { + /// Reset the peeking “cursor” + pub fn reset_peek(&mut self) { + self.index = 0 + } + + /// Works exactly like `.next()` with the only difference that it doesn't + /// advance itself. `.peek()` can be called multiple times, to peek + /// further ahead. + /// When `.next()` is called, reset the peeking “cursor”. + pub fn peek(&mut self) -> Option<&I::Item> { + let ret = if self.index < self.buf.len() { + Some(&self.buf[self.index]) + } else { + match self.iter.next() { + Some(x) => { + self.buf.push_back(x); + Some(&self.buf[self.index]) + } + None => return None, + } + }; + + self.index += 1; + ret + } +} + +impl PeekNth { + /// Works exactly like the `peek` method in [`std::iter::Peekable`]. + pub fn peek(&mut self) -> Option<&I::Item> { + self.peek_nth(0) + } +} + +impl Iterator for MultiPeekGeneral { + type Item = I::Item; + + fn next(&mut self) -> Option { + self.index.reset_index(); + self.buf.pop_front().or_else(|| self.iter.next()) + } + + fn size_hint(&self) -> (usize, Option) { + size_hint::add_scalar(self.iter.size_hint(), self.buf.len()) + } + + fn fold(self, mut init: B, mut f: F) -> B + where + F: FnMut(B, Self::Item) -> B, + { + init = self.buf.into_iter().fold(init, &mut f); + self.iter.fold(init, f) + } +} + +impl ExactSizeIterator for MultiPeekGeneral {} + +impl PeekingNext for MultiPeekGeneral +where + I: Iterator, +{ + fn peeking_next(&mut self, accept: F) -> Option + where + F: FnOnce(&Self::Item) -> bool, + { + self.peek_mut().filter(|item| accept(item))?; + self.next() + } +} From c130144c7a5fd611fc170dc8b474f99ca31045b9 Mon Sep 17 00:00:00 2001 From: Owen Leung Date: Fri, 17 May 2024 10:03:22 +0800 Subject: [PATCH 2/7] Remove multipeek_impl and peek_nth, revise lib.rs and other files for correct moduling --- src/free.rs | 4 +- src/lib.rs | 11 +-- src/multipeek_general.rs | 8 +- src/multipeek_impl.rs | 116 ------------------------- src/peek_nth.rs | 178 --------------------------------------- 5 files changed, 12 insertions(+), 305 deletions(-) delete mode 100644 src/multipeek_impl.rs delete mode 100644 src/peek_nth.rs diff --git a/src/free.rs b/src/free.rs index 8d0bcf3ea..59c38fac9 100644 --- a/src/free.rs +++ b/src/free.rs @@ -20,9 +20,9 @@ pub use crate::adaptors::{interleave, put_back}; pub use crate::kmerge_impl::kmerge; pub use crate::merge_join::{merge, merge_join_by}; #[cfg(feature = "use_alloc")] -pub use crate::multipeek_impl::multipeek; +pub use crate::multipeek_general::multipeek; #[cfg(feature = "use_alloc")] -pub use crate::peek_nth::peek_nth; +pub use crate::multipeek_general::peek_nth; #[cfg(feature = "use_alloc")] pub use crate::put_back_n_impl::put_back_n; #[cfg(feature = "use_alloc")] diff --git a/src/lib.rs b/src/lib.rs index aa4ffb086..aa0f041e7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -115,10 +115,10 @@ pub mod structs { pub use crate::kmerge_impl::{KMerge, KMergeBy}; pub use crate::merge_join::{Merge, MergeBy, MergeJoinBy}; #[cfg(feature = "use_alloc")] - pub use crate::multipeek_impl::MultiPeek; + pub use crate::multipeek_general::MultiPeek; pub use crate::pad_tail::PadUsing; #[cfg(feature = "use_alloc")] - pub use crate::peek_nth::PeekNth; + pub use crate::multipeek_general::PeekNth; pub use crate::peeking_take_while::PeekingTakeWhile; #[cfg(feature = "use_alloc")] pub use crate::permutations::Permutations; @@ -204,12 +204,9 @@ mod kmerge_impl; mod lazy_buffer; mod merge_join; mod minmax; -mod multipeek_general; #[cfg(feature = "use_alloc")] -mod multipeek_impl; +mod multipeek_general; mod pad_tail; -#[cfg(feature = "use_alloc")] -mod peek_nth; mod peeking_take_while; #[cfg(feature = "use_alloc")] mod permutations; @@ -4116,7 +4113,7 @@ pub trait Itertools: Iterator { where Self: Sized, { - multipeek_impl::multipeek(self) + multipeek_general::multipeek(self) } /// Collect the items in this iterator and return a `HashMap` which diff --git a/src/multipeek_general.rs b/src/multipeek_general.rs index 568b7fafe..759d8ed3d 100644 --- a/src/multipeek_general.rs +++ b/src/multipeek_general.rs @@ -1,4 +1,5 @@ #![allow(private_interfaces)] +#![allow(private_bounds)] use crate::{size_hint, PeekingNext}; use alloc::collections::VecDeque; @@ -7,13 +8,16 @@ use std::iter::Fuse; /// See [`multipeek()`] for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] #[derive(Debug, Clone)] -struct MultiPeekGeneral { +pub struct MultiPeekGeneral { pub iter: Fuse, pub buf: VecDeque, pub index: Idx, } +/// See [`multipeek()`] for more information. pub type MultiPeek = MultiPeekGeneral; + +/// See [`peek_nth()`] for more information. pub type PeekNth = MultiPeekGeneral; /// An iterator adaptor that allows the user to peek at multiple `.next()` @@ -49,7 +53,7 @@ where } } -trait PeekIndex { +pub trait PeekIndex { fn reset_index(&mut self); } diff --git a/src/multipeek_impl.rs b/src/multipeek_impl.rs deleted file mode 100644 index 6f800b6fb..000000000 --- a/src/multipeek_impl.rs +++ /dev/null @@ -1,116 +0,0 @@ -use crate::size_hint; -#[cfg(doc)] -use crate::Itertools; -use crate::PeekingNext; -use alloc::collections::VecDeque; -use std::iter::Fuse; - -/// See [`multipeek()`] for more information. -#[derive(Clone, Debug)] -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct MultiPeek -where - I: Iterator, -{ - iter: Fuse, - buf: VecDeque, - index: usize, -} - -/// An iterator adaptor that allows the user to peek at multiple `.next()` -/// values without advancing the base iterator. -/// -/// [`IntoIterator`] enabled version of [`Itertools::multipeek`]. -pub fn multipeek(iterable: I) -> MultiPeek -where - I: IntoIterator, -{ - MultiPeek { - iter: iterable.into_iter().fuse(), - buf: VecDeque::new(), - index: 0, - } -} - -impl MultiPeek -where - I: Iterator, -{ - /// Reset the peeking “cursor” - pub fn reset_peek(&mut self) { - self.index = 0; - } -} - -impl MultiPeek { - /// Works exactly like `.next()` with the only difference that it doesn't - /// advance itself. `.peek()` can be called multiple times, to peek - /// further ahead. - /// When `.next()` is called, reset the peeking “cursor”. - pub fn peek(&mut self) -> Option<&I::Item> { - let ret = if self.index < self.buf.len() { - Some(&self.buf[self.index]) - } else { - match self.iter.next() { - Some(x) => { - self.buf.push_back(x); - Some(&self.buf[self.index]) - } - None => return None, - } - }; - - self.index += 1; - ret - } -} - -impl PeekingNext for MultiPeek -where - I: Iterator, -{ - fn peeking_next(&mut self, accept: F) -> Option - where - F: FnOnce(&Self::Item) -> bool, - { - if self.buf.is_empty() { - if let Some(r) = self.peek() { - if !accept(r) { - return None; - } - } - } else if let Some(r) = self.buf.front() { - if !accept(r) { - return None; - } - } - self.next() - } -} - -impl Iterator for MultiPeek -where - I: Iterator, -{ - type Item = I::Item; - - fn next(&mut self) -> Option { - self.index = 0; - self.buf.pop_front().or_else(|| self.iter.next()) - } - - fn size_hint(&self) -> (usize, Option) { - size_hint::add_scalar(self.iter.size_hint(), self.buf.len()) - } - - fn fold(self, mut init: B, mut f: F) -> B - where - F: FnMut(B, Self::Item) -> B, - { - init = self.buf.into_iter().fold(init, &mut f); - self.iter.fold(init, f) - } -} - -// Same size -impl ExactSizeIterator for MultiPeek where I: ExactSizeIterator {} diff --git a/src/peek_nth.rs b/src/peek_nth.rs deleted file mode 100644 index b03a3ef5f..000000000 --- a/src/peek_nth.rs +++ /dev/null @@ -1,178 +0,0 @@ -use crate::size_hint; -use crate::PeekingNext; -use alloc::collections::VecDeque; -use std::iter::Fuse; - -/// See [`peek_nth()`] for more information. -#[derive(Clone, Debug)] -#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -pub struct PeekNth -where - I: Iterator, -{ - iter: Fuse, - buf: VecDeque, -} - -/// A drop-in replacement for [`std::iter::Peekable`] which adds a `peek_nth` -/// method allowing the user to `peek` at a value several iterations forward -/// without advancing the base iterator. -/// -/// This differs from `multipeek` in that subsequent calls to `peek` or -/// `peek_nth` will always return the same value until `next` is called -/// (making `reset_peek` unnecessary). -pub fn peek_nth(iterable: I) -> PeekNth -where - I: IntoIterator, -{ - PeekNth { - iter: iterable.into_iter().fuse(), - buf: VecDeque::new(), - } -} - -impl PeekNth -where - I: Iterator, -{ - /// Works exactly like the `peek` method in [`std::iter::Peekable`]. - pub fn peek(&mut self) -> Option<&I::Item> { - self.peek_nth(0) - } - - /// Works exactly like the `peek_mut` method in [`std::iter::Peekable`]. - pub fn peek_mut(&mut self) -> Option<&mut I::Item> { - self.peek_nth_mut(0) - } - - /// Returns a reference to the `nth` value without advancing the iterator. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use itertools::peek_nth; - /// - /// let xs = vec![1, 2, 3]; - /// let mut iter = peek_nth(xs.into_iter()); - /// - /// assert_eq!(iter.peek_nth(0), Some(&1)); - /// assert_eq!(iter.next(), Some(1)); - /// - /// // The iterator does not advance even if we call `peek_nth` multiple times - /// assert_eq!(iter.peek_nth(0), Some(&2)); - /// assert_eq!(iter.peek_nth(1), Some(&3)); - /// assert_eq!(iter.next(), Some(2)); - /// - /// // Calling `peek_nth` past the end of the iterator will return `None` - /// assert_eq!(iter.peek_nth(1), None); - /// ``` - pub fn peek_nth(&mut self, n: usize) -> Option<&I::Item> { - let unbuffered_items = (n + 1).saturating_sub(self.buf.len()); - - self.buf.extend(self.iter.by_ref().take(unbuffered_items)); - - self.buf.get(n) - } - - /// Returns a mutable reference to the `nth` value without advancing the iterator. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use itertools::peek_nth; - /// - /// let xs = vec![1, 2, 3, 4, 5]; - /// let mut iter = peek_nth(xs.into_iter()); - /// - /// assert_eq!(iter.peek_nth_mut(0), Some(&mut 1)); - /// assert_eq!(iter.next(), Some(1)); - /// - /// // The iterator does not advance even if we call `peek_nth_mut` multiple times - /// assert_eq!(iter.peek_nth_mut(0), Some(&mut 2)); - /// assert_eq!(iter.peek_nth_mut(1), Some(&mut 3)); - /// assert_eq!(iter.next(), Some(2)); - /// - /// // Peek into the iterator and set the value behind the mutable reference. - /// if let Some(p) = iter.peek_nth_mut(1) { - /// assert_eq!(*p, 4); - /// *p = 9; - /// } - /// - /// // The value we put in reappears as the iterator continues. - /// assert_eq!(iter.next(), Some(3)); - /// assert_eq!(iter.next(), Some(9)); - /// - /// // Calling `peek_nth_mut` past the end of the iterator will return `None` - /// assert_eq!(iter.peek_nth_mut(1), None); - /// ``` - pub fn peek_nth_mut(&mut self, n: usize) -> Option<&mut I::Item> { - let unbuffered_items = (n + 1).saturating_sub(self.buf.len()); - - self.buf.extend(self.iter.by_ref().take(unbuffered_items)); - - self.buf.get_mut(n) - } - - /// Works exactly like the `next_if` method in [`std::iter::Peekable`]. - pub fn next_if(&mut self, func: impl FnOnce(&I::Item) -> bool) -> Option { - match self.next() { - Some(item) if func(&item) => Some(item), - Some(item) => { - self.buf.push_front(item); - None - } - _ => None, - } - } - - /// Works exactly like the `next_if_eq` method in [`std::iter::Peekable`]. - pub fn next_if_eq(&mut self, expected: &T) -> Option - where - T: ?Sized, - I::Item: PartialEq, - { - self.next_if(|next| next == expected) - } -} - -impl Iterator for PeekNth -where - I: Iterator, -{ - type Item = I::Item; - - fn next(&mut self) -> Option { - self.buf.pop_front().or_else(|| self.iter.next()) - } - - fn size_hint(&self) -> (usize, Option) { - size_hint::add_scalar(self.iter.size_hint(), self.buf.len()) - } - - fn fold(self, mut init: B, mut f: F) -> B - where - F: FnMut(B, Self::Item) -> B, - { - init = self.buf.into_iter().fold(init, &mut f); - self.iter.fold(init, f) - } -} - -impl ExactSizeIterator for PeekNth where I: ExactSizeIterator {} - -impl PeekingNext for PeekNth -where - I: Iterator, -{ - fn peeking_next(&mut self, accept: F) -> Option - where - F: FnOnce(&Self::Item) -> bool, - { - self.peek().filter(|item| accept(item))?; - self.next() - } -} From 518c877dc028882312a453ad6a43c4f6572b9dba Mon Sep 17 00:00:00 2001 From: Owen Leung Date: Fri, 17 May 2024 10:17:00 +0800 Subject: [PATCH 3/7] fix cargo fmt, cargo doc and rebase master --- .github/workflows/ci.yml | 3 ++- CHANGELOG.md | 44 ++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 +- src/multipeek_general.rs | 2 +- 4 files changed, 48 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 239ce2405..df6bf412c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,9 +69,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@stable - uses: obi1kenobi/cargo-semver-checks-action@v2.4 with: - rust-toolchain: stable + rust-toolchain: manual feature-group: all-features # Used to signal to branch protections that all other jobs have succeeded. diff --git a/CHANGELOG.md b/CHANGELOG.md index 3994e5ce7..de9564c6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,49 @@ # Changelog +## 0.13.0 + +### Breaking +- Removed implementation of `DoubleEndedIterator` for `ConsTuples` (#853) +- Made `MultiProduct` fused and fixed on an empty iterator (#835, #834) +- Changed `iproduct!` to return tuples for maxi one iterator too (#870) +- Changed `PutBack::put_back` to return the old value (#880) +- Removed deprecated `repeat_call, Itertools::{foreach, step, map_results, fold_results}` (#878) +- Removed `TakeWhileInclusive::new` (#912) + +### Added +- Added `Itertools::{smallest_by, smallest_by_key, largest, largest_by, largest_by_key}` (#654, #885) +- Added `Itertools::tail` (#899) +- Implemented `DoubleEndedIterator` for `ProcessResults` (#910) +- Implemented `Debug` for `FormatWith` (#931) +- Added `Itertools::get` (#891) + +### Changed +- Deprecated `Itertools::group_by` (renamed `chunk_by`) (#866, #879) +- Deprecated `unfold` (use `std::iter::from_fn` instead) (#871) +- Optimized `GroupingMapBy` (#873, #876) +- Relaxed `Fn` bounds to `FnMut` in `diff_with, Itertools::into_group_map_by` (#886) +- Relaxed `Debug/Clone` bounds for `MapInto` (#889) +- Documented the `use_alloc` feature (#887) +- Optimized `Itertools::set_from` (#888) +- Removed badges in `README.md` (#890) +- Added "no-std" categories in `Cargo.toml` (#894) +- Fixed `Itertools::k_smallest` on short unfused iterators (#900) +- Deprecated `Itertools::tree_fold1` (renamed `tree_reduce`) (#895) +- Deprecated `GroupingMap::fold_first` (renamed `reduce`) (#902) +- Fixed `Itertools::k_smallest(0)` to consume the iterator, optimized `Itertools::k_smallest(1)` (#909) +- Specialized `Combinations::nth` (#914) +- Specialized `MergeBy::fold` (#920) +- Specialized `CombinationsWithReplacement::nth` (#923) +- Specialized `FlattenOk::{fold, rfold}` (#927) +- Specialized `Powerset::nth` (#924) +- Documentation fixes (#882, #936) +- Fixed `assert_equal` for iterators longer than `i32::MAX` (#932) +- Updated the `must_use` message of non-lazy `KMergeBy` and `TupleCombinations` (#939) + +### Notable Internal Changes +- Tested iterator laziness (#792) +- Created `CONTRIBUTING.md` (#767) + ## 0.12.1 ### Added diff --git a/src/lib.rs b/src/lib.rs index aa0f041e7..49ffaf897 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -116,9 +116,9 @@ pub mod structs { pub use crate::merge_join::{Merge, MergeBy, MergeJoinBy}; #[cfg(feature = "use_alloc")] pub use crate::multipeek_general::MultiPeek; - pub use crate::pad_tail::PadUsing; #[cfg(feature = "use_alloc")] pub use crate::multipeek_general::PeekNth; + pub use crate::pad_tail::PadUsing; pub use crate::peeking_take_while::PeekingTakeWhile; #[cfg(feature = "use_alloc")] pub use crate::permutations::Permutations; diff --git a/src/multipeek_general.rs b/src/multipeek_general.rs index 759d8ed3d..a4f5c5e2e 100644 --- a/src/multipeek_general.rs +++ b/src/multipeek_general.rs @@ -23,7 +23,7 @@ pub type PeekNth = MultiPeekGeneral; /// An iterator adaptor that allows the user to peek at multiple `.next()` /// values without advancing the base iterator. /// -/// [`IntoIterator`] enabled version of [`Itertools::multipeek`]. +/// [`IntoIterator`] enabled version of [`crate::Itertools::multipeek`]. pub fn multipeek(iterable: I) -> MultiPeek where I: IntoIterator, From 3fab1c2e805b7df549df45b10d1163d46ed56690 Mon Sep 17 00:00:00 2001 From: Owen Leung Date: Fri, 17 May 2024 18:21:43 +0800 Subject: [PATCH 4/7] Address some of the comments --- src/free.rs | 4 +--- src/lib.rs | 4 +--- src/multipeek_general.rs | 15 ++++++--------- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/src/free.rs b/src/free.rs index 59c38fac9..852ae0ede 100644 --- a/src/free.rs +++ b/src/free.rs @@ -20,9 +20,7 @@ pub use crate::adaptors::{interleave, put_back}; pub use crate::kmerge_impl::kmerge; pub use crate::merge_join::{merge, merge_join_by}; #[cfg(feature = "use_alloc")] -pub use crate::multipeek_general::multipeek; -#[cfg(feature = "use_alloc")] -pub use crate::multipeek_general::peek_nth; +pub use crate::multipeek_general::{multipeek, peek_nth}; #[cfg(feature = "use_alloc")] pub use crate::put_back_n_impl::put_back_n; #[cfg(feature = "use_alloc")] diff --git a/src/lib.rs b/src/lib.rs index 49ffaf897..ce918d847 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -115,9 +115,7 @@ pub mod structs { pub use crate::kmerge_impl::{KMerge, KMergeBy}; pub use crate::merge_join::{Merge, MergeBy, MergeJoinBy}; #[cfg(feature = "use_alloc")] - pub use crate::multipeek_general::MultiPeek; - #[cfg(feature = "use_alloc")] - pub use crate::multipeek_general::PeekNth; + pub use crate::multipeek_general::{MultiPeek, PeekNth}; pub use crate::pad_tail::PadUsing; pub use crate::peeking_take_while::PeekingTakeWhile; #[cfg(feature = "use_alloc")] diff --git a/src/multipeek_general.rs b/src/multipeek_general.rs index a4f5c5e2e..4d21c7a4a 100644 --- a/src/multipeek_general.rs +++ b/src/multipeek_general.rs @@ -1,17 +1,13 @@ -#![allow(private_interfaces)] -#![allow(private_bounds)] - use crate::{size_hint, PeekingNext}; use alloc::collections::VecDeque; use std::iter::Fuse; -/// See [`multipeek()`] for more information. #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] #[derive(Debug, Clone)] pub struct MultiPeekGeneral { - pub iter: Fuse, - pub buf: VecDeque, - pub index: Idx, + iter: Fuse, + buf: VecDeque, + index: Idx, } /// See [`multipeek()`] for more information. @@ -23,7 +19,7 @@ pub type PeekNth = MultiPeekGeneral; /// An iterator adaptor that allows the user to peek at multiple `.next()` /// values without advancing the base iterator. /// -/// [`IntoIterator`] enabled version of [`crate::Itertools::multipeek`]. +/// [`IntoIterator`] enabled version of [`Itertools::multipeek`](crate::Itertools::multipeek). pub fn multipeek(iterable: I) -> MultiPeek where I: IntoIterator, @@ -241,9 +237,10 @@ impl Iterator for MultiPeekGeneral { impl ExactSizeIterator for MultiPeekGeneral {} -impl PeekingNext for MultiPeekGeneral +impl PeekingNext for MultiPeekGeneral where I: Iterator, + Idx: PeekIndex, { fn peeking_next(&mut self, accept: F) -> Option where From 4bf4e4b2850d2ee137e893efde60a8aece56a32a Mon Sep 17 00:00:00 2001 From: Owen Leung Date: Fri, 17 May 2024 18:45:12 +0800 Subject: [PATCH 5/7] Put peek into multipeek_general, and remove implementation for PeekNth and MultiPeek --- src/multipeek_general.rs | 68 +++++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 28 deletions(-) diff --git a/src/multipeek_general.rs b/src/multipeek_general.rs index 4d21c7a4a..b28263aa2 100644 --- a/src/multipeek_general.rs +++ b/src/multipeek_general.rs @@ -51,19 +51,59 @@ where pub trait PeekIndex { fn reset_index(&mut self); + fn increment_index(&mut self); + fn index(&self) -> usize; } impl PeekIndex for () { fn reset_index(&mut self) {} + fn increment_index(&mut self) {} + fn index(&self) -> usize { + 0 + } } impl PeekIndex for usize { fn reset_index(&mut self) { *self = 0; } + fn increment_index(&mut self) { + *self += 1 + } + fn index(&self) -> usize { + *self + } } impl MultiPeekGeneral { + /// Works similarly to `.next()`, but does not advance the iterator itself. + /// + /// - For `MultiPeekGeneral`, calling `.peek()` will increment the internal index, + /// so the next call to `.peek()` will advance to the next element in the buffer. + /// The actual underlying iterator is not consumed, allowing multiple peeks + /// without advancing the iterator itself. + /// - For `peek_nth`, since there is no internal index used, calling `.peek()` + /// multiple times will not advance the internal state or the iterator, providing + /// a consistent view of the same element. + pub fn peek(&mut self) -> Option<&I::Item> { + // self.index.increment_index(); + // self.peek_nth(0) + let ret = if self.index.index() < self.buf.len() { + Some(&self.buf[self.index.index()]) + } else { + match self.iter.next() { + Some(x) => { + self.buf.push_back(x); + Some(&self.buf[self.index.index()]) + } + None => return None, + } + }; + + self.index.increment_index(); + ret + } + /// Works exactly like the `peek_mut` method in [`std::iter::Peekable`]. pub fn peek_mut(&mut self) -> Option<&mut I::Item> { self.peek_nth_mut(0) @@ -184,34 +224,6 @@ impl MultiPeek { pub fn reset_peek(&mut self) { self.index = 0 } - - /// Works exactly like `.next()` with the only difference that it doesn't - /// advance itself. `.peek()` can be called multiple times, to peek - /// further ahead. - /// When `.next()` is called, reset the peeking “cursor”. - pub fn peek(&mut self) -> Option<&I::Item> { - let ret = if self.index < self.buf.len() { - Some(&self.buf[self.index]) - } else { - match self.iter.next() { - Some(x) => { - self.buf.push_back(x); - Some(&self.buf[self.index]) - } - None => return None, - } - }; - - self.index += 1; - ret - } -} - -impl PeekNth { - /// Works exactly like the `peek` method in [`std::iter::Peekable`]. - pub fn peek(&mut self) -> Option<&I::Item> { - self.peek_nth(0) - } } impl Iterator for MultiPeekGeneral { From 3920dbe5219c1dbabc009869e900e14c4771e88f Mon Sep 17 00:00:00 2001 From: Owen Leung Date: Fri, 17 May 2024 19:06:16 +0800 Subject: [PATCH 6/7] Fix peeking_next --- src/multipeek_general.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/multipeek_general.rs b/src/multipeek_general.rs index b28263aa2..64675d2d6 100644 --- a/src/multipeek_general.rs +++ b/src/multipeek_general.rs @@ -86,8 +86,6 @@ impl MultiPeekGeneral { /// multiple times will not advance the internal state or the iterator, providing /// a consistent view of the same element. pub fn peek(&mut self) -> Option<&I::Item> { - // self.index.increment_index(); - // self.peek_nth(0) let ret = if self.index.index() < self.buf.len() { Some(&self.buf[self.index.index()]) } else { @@ -258,7 +256,17 @@ where where F: FnOnce(&Self::Item) -> bool, { - self.peek_mut().filter(|item| accept(item))?; + if self.buf.is_empty() { + if let Some(r) = self.peek() { + if !accept(r) { + return None; + } + } + } else if let Some(r) = self.buf.front() { + if !accept(r) { + return None; + } + } self.next() } } From 4fc8456d62f40c8dfb947ea3a790055df608986f Mon Sep 17 00:00:00 2001 From: Owen Leung Date: Sun, 19 May 2024 11:54:28 +0800 Subject: [PATCH 7/7] Change increment_index to generic add, add test to test peek_nth and peek --- src/multipeek_general.rs | 21 ++++++++++++++------- tests/test_std.rs | 14 ++++++++++++++ 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/multipeek_general.rs b/src/multipeek_general.rs index 64675d2d6..6759b735f 100644 --- a/src/multipeek_general.rs +++ b/src/multipeek_general.rs @@ -51,13 +51,13 @@ where pub trait PeekIndex { fn reset_index(&mut self); - fn increment_index(&mut self); + fn add(&mut self, n: usize); fn index(&self) -> usize; } impl PeekIndex for () { fn reset_index(&mut self) {} - fn increment_index(&mut self) {} + fn add(&mut self, _n: usize) {} fn index(&self) -> usize { 0 } @@ -67,8 +67,8 @@ impl PeekIndex for usize { fn reset_index(&mut self) { *self = 0; } - fn increment_index(&mut self) { - *self += 1 + fn add(&mut self, n: usize) { + *self += n } fn index(&self) -> usize { *self @@ -97,8 +97,7 @@ impl MultiPeekGeneral { None => return None, } }; - - self.index.increment_index(); + self.index.add(1); ret } @@ -135,7 +134,13 @@ impl MultiPeekGeneral { self.buf.extend(self.iter.by_ref().take(unbuffered_items)); - self.buf.get(n) + let ret = self.buf.get(n); + + if ret.is_some() { + self.index.add(n + 1); + } + + ret } /// Returns a mutable reference to the `nth` value without advancing the iterator. @@ -172,6 +177,8 @@ impl MultiPeekGeneral { /// assert_eq!(iter.peek_nth_mut(1), None); /// ``` pub fn peek_nth_mut(&mut self, n: usize) -> Option<&mut I::Item> { + self.index.add(n); + let unbuffered_items = (n + 1).saturating_sub(self.buf.len()); self.buf.extend(self.iter.by_ref().take(unbuffered_items)); diff --git a/tests/test_std.rs b/tests/test_std.rs index 00246d506..ef55f190d 100644 --- a/tests/test_std.rs +++ b/tests/test_std.rs @@ -660,6 +660,20 @@ fn test_multipeek() { assert_eq!(mp.peek(), None); } +#[test] +fn test_multipeek_peeknth() { + let nums = vec![6, 7, 8, 9, 10]; + + let mut mp = multipeek(nums); + assert_eq!(mp.peek_nth(2), Some(&8)); + assert_eq!(mp.peek(), Some(&9)); + assert_eq!(mp.peek(), Some(&10)); + mp.reset_peek(); + assert_eq!(mp.peek_nth(1), Some(&7)); + assert_eq!(mp.peek_nth(3), Some(&9)); + assert_eq!(mp.peek_nth(10), None); +} + #[test] fn test_multipeek_reset() { let data = [1, 2, 3, 4];