diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-04-28 15:59:25 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-04-28 15:59:25 +0000 |
commit | dc827c69e2060f747991536e10cf9951c91bbc51 (patch) | |
tree | d7d800f84b0d4ccd703059529235b632ba6bc92c | |
parent | d04df4fff8360dc1121f66473dad7903a09d8201 (diff) | |
parent | e02c202c4051e7f57491ef1f85dc1ed8ba820b79 (diff) | |
download | itertools-android13-frc-os-statsd-release.tar.gz |
Snap for 8512216 from e02c202c4051e7f57491ef1f85dc1ed8ba820b79 to tm-frc-os-statsd-releaset_frc_sta_330443010android13-frc-os-statsd-release
Change-Id: I3b842d3586b9aa120afabc80fe39c02eaf671a89
-rw-r--r-- | .cargo_vcs_info.json | 2 | ||||
-rw-r--r-- | Android.bp | 2 | ||||
-rw-r--r-- | CHANGELOG.md | 11 | ||||
-rw-r--r-- | Cargo.toml | 3 | ||||
-rw-r--r-- | Cargo.toml.orig | 3 | ||||
-rw-r--r-- | METADATA | 10 | ||||
-rw-r--r-- | README.md (renamed from README.rst) | 43 | ||||
-rw-r--r-- | TEST_MAPPING | 6 | ||||
-rw-r--r-- | src/adaptors/coalesce.rs | 8 | ||||
-rw-r--r-- | src/adaptors/map.rs | 8 | ||||
-rw-r--r-- | src/adaptors/mod.rs | 30 | ||||
-rw-r--r-- | src/adaptors/multi_product.rs | 8 | ||||
-rw-r--r-- | src/duplicates_impl.rs | 23 | ||||
-rw-r--r-- | src/free.rs | 37 | ||||
-rw-r--r-- | src/impl_macros.rs | 2 | ||||
-rw-r--r-- | src/kmerge_impl.rs | 2 | ||||
-rw-r--r-- | src/lib.rs | 79 | ||||
-rw-r--r-- | src/multipeek_impl.rs | 1 | ||||
-rw-r--r-- | src/pad_tail.rs | 7 | ||||
-rw-r--r-- | src/peeking_take_while.rs | 7 | ||||
-rw-r--r-- | src/process_results_impl.rs | 17 | ||||
-rw-r--r-- | src/tuple_impl.rs | 4 | ||||
-rw-r--r-- | src/unziptuple.rs | 80 | ||||
-rw-r--r-- | tests/fold_specialization.rs | 13 | ||||
-rw-r--r-- | tests/specializations.rs | 85 | ||||
-rw-r--r-- | tests/test_core.rs | 19 | ||||
-rw-r--r-- | tests/test_std.rs | 40 |
27 files changed, 464 insertions, 86 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index b29a8ee..5b491ea 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,5 +1,5 @@ { "git": { - "sha1": "20c09bd4fed037fa3df6f2a6a1e717b01be89f11" + "sha1": "3003c2a968f144cbccb63c768d2fec2e83a69ca4" } } @@ -43,7 +43,7 @@ rust_library { host_supported: true, crate_name: "itertools", cargo_env_compat: true, - cargo_pkg_version: "0.10.1", + cargo_pkg_version: "0.10.3", srcs: ["src/lib.rs"], edition: "2018", features: [ diff --git a/CHANGELOG.md b/CHANGELOG.md index 41c4ab0..5e2032c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## 0.10.2 + - Add `Itertools::multiunzip` (#362, #565) + - Add `intersperse` and `intersperse_with` free functions (#555) + - Add `Itertools::sorted_by_cached_key` (#424, #575) + - Specialize `ProcessResults::fold` (#563) + - Fix subtraction overflow in `DuplicatesBy::size_hint` (#552) + - Fix specialization tests (#574) + - More `Debug` impls (#573) + - Deprecate `fold1` (use `reduce` instead) (#580) + - Documentation fixes (`HomogenousTuple`, `into_group_map`, `into_group_map_by`, `MultiPeek::peek`) (#543 et al.) + ## 0.10.1 - Add `Itertools::contains` (#514) - Add `Itertools::counts_by` (#515) @@ -13,11 +13,12 @@ [package] edition = "2018" name = "itertools" -version = "0.10.1" +version = "0.10.3" authors = ["bluss"] exclude = ["/bors.toml"] description = "Extra iterator adaptors, iterator methods, free functions, and macros." documentation = "https://docs.rs/itertools/" +readme = "README.md" keywords = ["iterator", "data-structure", "zip", "product", "group-by"] categories = ["algorithms", "rust-patterns"] license = "MIT/Apache-2.0" diff --git a/Cargo.toml.orig b/Cargo.toml.orig index d7f6b23..22a08a8 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -1,11 +1,12 @@ [package] name = "itertools" -version = "0.10.1" +version = "0.10.3" license = "MIT/Apache-2.0" repository = "https://github.com/rust-itertools/itertools" documentation = "https://docs.rs/itertools/" authors = ["bluss"] +readme = "README.md" description = "Extra iterator adaptors, iterator methods, free functions, and macros." @@ -7,13 +7,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/itertools/itertools-0.10.1.crate" + value: "https://static.crates.io/crates/itertools/itertools-0.10.3.crate" } - version: "0.10.1" + version: "0.10.3" license_type: NOTICE last_upgrade_date { - year: 2021 - month: 6 - day: 21 + year: 2022 + month: 3 + day: 1 } } @@ -1,50 +1,39 @@ - -Itertools -========= +# Itertools Extra iterator adaptors, functions and macros. -Please read the `API documentation here`__ - -__ https://docs.rs/itertools/ - -|build_status|_ |crates|_ +Please read the [API documentation here](https://docs.rs/itertools/). -.. |build_status| image:: https://travis-ci.org/rust-itertools/itertools.svg?branch=master -.. _build_status: https://travis-ci.org/rust-itertools/itertools +[![build_status](https://github.com/rust-itertools/itertools/actions/workflows/ci.yml/badge.svg)](https://github.com/rust-itertools/itertools/actions) +[![crates.io](https://img.shields.io/crates/v/itertools.svg)](https://crates.io/crates/itertools) -.. |crates| image:: https://meritbadge.herokuapp.com/itertools -.. _crates: https://crates.io/crates/itertools +How to use with Cargo: -How to use with cargo: - -.. code:: toml - - [dependencies] - itertools = "0.10.0" +```toml +[dependencies] +itertools = "0.10.2" +``` How to use in your crate: -.. code:: rust - - use itertools::Itertools; +```rust +use itertools::Itertools; +``` -How to contribute ------------------ +## How to contribute - Fix a bug or implement a new thing -- Include tests for your new feature, preferably a quickcheck test +- Include tests for your new feature, preferably a QuickCheck test - Make a Pull Request -For new features, please first consider filing a PR to `rust-lang/rust <https://github.com/rust-lang/rust>`_, +For new features, please first consider filing a PR to [rust-lang/rust](https://github.com/rust-lang/rust), adding your new feature to the `Iterator` trait of the standard library, if you believe it is reasonable. If it isn't accepted there, proposing it for inclusion in ``itertools`` is a good idea. The reason for doing is this is so that we avoid future breakage as with ``.flatten()``. However, if your feature involves heap allocation, such as storing elements in a ``Vec<T>``, then it can't be accepted into ``libcore``, and you should propose it for ``itertools`` directly instead. -License -------- +## License Dual-licensed to be compatible with the Rust project. diff --git a/TEST_MAPPING b/TEST_MAPPING index f6e148f..86d1024 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -17,11 +17,17 @@ "presubmit": [ { "name": "apkdmverity.test" + }, + { + "name": "microdroid_manager_test" } ], "presubmit-rust": [ { "name": "apkdmverity.test" + }, + { + "name": "microdroid_manager_test" } ] } diff --git a/src/adaptors/coalesce.rs b/src/adaptors/coalesce.rs index 1afbee5..b1aff6e 100644 --- a/src/adaptors/coalesce.rs +++ b/src/adaptors/coalesce.rs @@ -119,6 +119,10 @@ pub type DedupBy<I, Pred> = CoalesceBy<I, DedupPred2CoalescePred<Pred>, <I as It #[derive(Clone)] pub struct DedupPred2CoalescePred<DP>(DP); +impl<DP> fmt::Debug for DedupPred2CoalescePred<DP> { + debug_fmt_fields!(DedupPred2CoalescePred,); +} + pub trait DedupPredicate<T> { // TODO replace by Fn(&T, &T)->bool once Rust supports it fn dedup_pair(&mut self, a: &T, b: &T) -> bool; @@ -137,7 +141,7 @@ where } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct DedupEq; impl<T: PartialEq> DedupPredicate<T> for DedupEq { @@ -186,7 +190,7 @@ where pub type DedupByWithCount<I, Pred> = CoalesceBy<I, DedupPredWithCount2CoalescePred<Pred>, (usize, <I as Iterator>::Item)>; -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct DedupPredWithCount2CoalescePred<DP>(DP); impl<DP, T> CoalescePredicate<T, (usize, T)> for DedupPredWithCount2CoalescePred<DP> diff --git a/src/adaptors/map.rs b/src/adaptors/map.rs index eee5cc3..cf5e5a0 100644 --- a/src/adaptors/map.rs +++ b/src/adaptors/map.rs @@ -1,7 +1,7 @@ use std::iter::FromIterator; use std::marker::PhantomData; -#[derive(Clone)] +#[derive(Clone, Debug)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct MapSpecialCase<I, F> { iter: I, @@ -84,6 +84,10 @@ where #[derive(Clone)] pub struct MapSpecialCaseFnOk<F>(F); +impl<F> std::fmt::Debug for MapSpecialCaseFnOk<F> { + debug_fmt_fields!(MapSpecialCaseFnOk,); +} + /// Create a new `MapOk` iterator. pub fn map_ok<I, F, T, U, E>(iter: I, f: F) -> MapOk<I, F> where @@ -108,7 +112,7 @@ impl<T: Into<U>, U> MapSpecialCaseFn<T> for MapSpecialCaseFnInto<U> { } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct MapSpecialCaseFnInto<U>(PhantomData<U>); /// Create a new [`MapInto`] iterator. diff --git a/src/adaptors/mod.rs b/src/adaptors/mod.rs index bd0d93a..2010f53 100644 --- a/src/adaptors/mod.rs +++ b/src/adaptors/mod.rs @@ -477,7 +477,7 @@ pub trait MergePredicate<T> { fn merge_pred(&mut self, a: &T, b: &T) -> bool; } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct MergeLte; impl<T: PartialOrd> MergePredicate<T> for MergeLte { @@ -849,6 +849,13 @@ pub struct FilterOk<I, F> { f: F } +impl<I, F> fmt::Debug for FilterOk<I, F> +where + I: fmt::Debug, +{ + debug_fmt_fields!(FilterOk, iter); +} + /// Create a new `FilterOk` iterator. pub fn filter_ok<I, F, T, E>(iter: I, f: F) -> FilterOk<I, F> where I: Iterator<Item = Result<T, E>>, @@ -917,6 +924,13 @@ pub struct FilterMapOk<I, F> { f: F } +impl<I, F> fmt::Debug for FilterMapOk<I, F> +where + I: fmt::Debug, +{ + debug_fmt_fields!(FilterMapOk, iter); +} + fn transpose_result<T, E>(result: Result<Option<T>, E>) -> Option<Result<T, E>> { match result { Ok(Some(v)) => Some(Ok(v)), @@ -995,6 +1009,13 @@ pub struct Positions<I, F> { count: usize, } +impl<I, F> fmt::Debug for Positions<I, F> +where + I: fmt::Debug, +{ + debug_fmt_fields!(Positions, iter, count); +} + /// Create a new `Positions` iterator. pub fn positions<I, F>(iter: I, f: F) -> Positions<I, F> where I: Iterator, @@ -1058,6 +1079,13 @@ pub struct Update<I, F> { f: F, } +impl<I, F> fmt::Debug for Update<I, F> +where + I: fmt::Debug, +{ + debug_fmt_fields!(Update, iter); +} + /// Create a new `Update` iterator. pub fn update<I, F>(iter: I, f: F) -> Update<I, F> where diff --git a/src/adaptors/multi_product.rs b/src/adaptors/multi_product.rs index 9708ef4..30650ed 100644 --- a/src/adaptors/multi_product.rs +++ b/src/adaptors/multi_product.rs @@ -18,6 +18,14 @@ pub struct MultiProduct<I>(Vec<MultiProductIter<I>>) where I: Iterator + Clone, I::Item: Clone; +impl<I> std::fmt::Debug for MultiProduct<I> +where + I: Iterator + Clone + std::fmt::Debug, + I::Item: Clone + std::fmt::Debug, +{ + debug_fmt_fields!(CoalesceBy, 0); +} + /// Create a new cartesian product iterator over an arbitrary number /// of iterators of the same type. /// diff --git a/src/duplicates_impl.rs b/src/duplicates_impl.rs index 42049df..640d481 100644 --- a/src/duplicates_impl.rs +++ b/src/duplicates_impl.rs @@ -84,13 +84,18 @@ mod private { #[inline] fn size_hint(&self) -> (usize, Option<usize>) { let (_, hi) = self.iter.size_hint(); - // There are `hi` number of items left in the base iterator. In the best case scenario, - // these items are exactly the same as the ones pending (i.e items seen exactly once so - // far), plus (hi - pending) / 2 pairs of never seen before items. let hi = hi.map(|hi| { - let max_pending = std::cmp::min(self.meta.pending, hi); - let max_new = std::cmp::max(hi - self.meta.pending, 0) / 2; - max_pending + max_new + if hi <= self.meta.pending { + // fewer or equally many iter-remaining elements than pending elements + // => at most, each iter-remaining element is matched + hi + } else { + // fewer pending elements than iter-remaining elements + // => at most: + // * each pending element is matched + // * the other iter-remaining elements come in pairs + self.meta.pending + (hi - self.meta.pending) / 2 + } }); // The lower bound is always 0 since we might only get unique items from now on (0, hi) @@ -117,6 +122,7 @@ mod private { } /// Apply the identity function to elements before checking them for equality. + #[derive(Debug)] pub struct ById; impl<V> KeyMethod<V, V> for ById { type Container = JustValue<V>; @@ -128,6 +134,9 @@ mod private { /// Apply a user-supplied function to elements before checking them for equality. pub struct ByFn<F>(pub(crate) F); + impl<F> fmt::Debug for ByFn<F> { + debug_fmt_fields!(ByFn,); + } impl<K, V, F> KeyMethod<K, V> for ByFn<F> where F: FnMut(&V) -> K, @@ -147,6 +156,7 @@ mod private { fn value(self) -> V; } + #[derive(Debug)] pub struct KeyValue<K, V>(K, V); impl<K, V> KeyXorValue<K, V> for KeyValue<K, V> { fn key_ref(&self) -> &K { @@ -160,6 +170,7 @@ mod private { } } + #[derive(Debug)] pub struct JustValue<V>(V); impl<V> KeyXorValue<V, V> for JustValue<V> { fn key_ref(&self) -> &V { diff --git a/src/free.rs b/src/free.rs index c78dc1d..6674030 100644 --- a/src/free.rs +++ b/src/free.rs @@ -14,8 +14,8 @@ use alloc::{ string::String, }; -#[cfg(feature = "use_alloc")] use crate::Itertools; +use crate::intersperse::{Intersperse, IntersperseWith}; pub use crate::adaptors::{ interleave, @@ -35,6 +35,41 @@ pub use crate::merge_join::merge_join_by; #[cfg(feature = "use_alloc")] pub use crate::rciter_impl::rciter; +/// Iterate `iterable` with a particular value inserted between each element. +/// +/// [`IntoIterator`] enabled version of [`Iterator::intersperse`]. +/// +/// ``` +/// use itertools::intersperse; +/// +/// itertools::assert_equal(intersperse((0..3), 8), vec![0, 8, 1, 8, 2]); +/// ``` +pub fn intersperse<I>(iterable: I, element: I::Item) -> Intersperse<I::IntoIter> + where I: IntoIterator, + <I as IntoIterator>::Item: Clone +{ + Itertools::intersperse(iterable.into_iter(), element) +} + +/// Iterate `iterable` with a particular value created by a function inserted +/// between each element. +/// +/// [`IntoIterator`] enabled version of [`Iterator::intersperse_with`]. +/// +/// ``` +/// use itertools::intersperse_with; +/// +/// let mut i = 10; +/// itertools::assert_equal(intersperse_with((0..3), || { i -= 1; i }), vec![0, 9, 1, 8, 2]); +/// assert_eq!(i, 8); +/// ``` +pub fn intersperse_with<I, F>(iterable: I, element: F) -> IntersperseWith<I::IntoIter, F> + where I: IntoIterator, + F: FnMut() -> I::Item +{ + Itertools::intersperse_with(iterable.into_iter(), element) +} + /// Iterate `iterable` with a running index. /// /// [`IntoIterator`] enabled version of [`Iterator::enumerate`]. diff --git a/src/impl_macros.rs b/src/impl_macros.rs index 3da8c63..5772bae 100644 --- a/src/impl_macros.rs +++ b/src/impl_macros.rs @@ -2,7 +2,7 @@ //! Implementation's internal macros macro_rules! debug_fmt_fields { - ($tyname:ident, $($($field:ident).+),*) => { + ($tyname:ident, $($($field:tt/*TODO ideally we would accept ident or tuple element here*/).+),*) => { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { f.debug_struct(stringify!($tyname)) $( diff --git a/src/kmerge_impl.rs b/src/kmerge_impl.rs index dce5b78..bd56b03 100644 --- a/src/kmerge_impl.rs +++ b/src/kmerge_impl.rs @@ -111,7 +111,7 @@ pub trait KMergePredicate<T> { fn kmerge_pred(&mut self, a: &T, b: &T) -> bool; } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct KMergeByLt; impl<T: PartialOrd> KMergePredicate<T> for KMergeByLt { @@ -179,6 +179,7 @@ pub use crate::repeatn::repeat_n; #[allow(deprecated)] pub use crate::sources::{repeat_call, unfold, iterate}; pub use crate::with_position::Position; +pub use crate::unziptuple::{multiunzip, MultiUnzip}; pub use crate::ziptuple::multizip; mod adaptors; mod either_or_both; @@ -237,6 +238,7 @@ mod tuple_impl; mod duplicates_impl; #[cfg(feature = "use_std")] mod unique_impl; +mod unziptuple; mod with_position; mod zip_eq_impl; mod zip_longest; @@ -660,7 +662,7 @@ pub trait Itertools : Iterator { } /// Return an iterator over all contiguous windows producing tuples of - /// a specific size (up to 4). + /// a specific size (up to 12). /// /// `tuple_windows` clones the iterator elements so that they can be /// part of successive windows, this makes it most suited for iterators @@ -702,7 +704,7 @@ pub trait Itertools : Iterator { /// 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). + /// iterator, producing tuples of a specific size (up to 12). /// /// `circular_tuple_windows` clones the iterator elements so that they can be /// part of successive windows, this makes it most suited for iterators @@ -735,7 +737,7 @@ pub trait Itertools : Iterator { tuple_impl::circular_tuple_windows(self) } /// Return an iterator that groups the items in tuples of a specific size - /// (up to 4). + /// (up to 12). /// /// See also the method [`.next_tuple()`](Itertools::next_tuple). /// @@ -2241,6 +2243,7 @@ pub trait Itertools : Iterator { /// assert_eq!((0..10).fold1(|x, y| x + y).unwrap_or(0), 45); /// assert_eq!((0..0).fold1(|x, y| x * y), None); /// ``` + #[deprecated(since = "0.10.2", note = "Use `Iterator::reduce` instead")] fn fold1<F>(mut self, f: F) -> Option<Self::Item> where F: FnMut(Self::Item, Self::Item) -> Self::Item, Self: Sized, @@ -2665,6 +2668,43 @@ pub trait Itertools : Iterator { v.into_iter() } + /// Sort all iterator elements into a new iterator in ascending order. The key function is + /// called exactly once per key. + /// + /// **Note:** This consumes the entire iterator, uses the + /// [`slice::sort_by_cached_key`] method and returns the result as a new + /// iterator that owns its elements. + /// + /// The sorted iterator, if directly collected to a `Vec`, is converted + /// without any extra copying or allocation cost. + /// + /// ``` + /// use itertools::Itertools; + /// + /// // sort people in descending order by age + /// let people = vec![("Jane", 20), ("John", 18), ("Jill", 30), ("Jack", 27)]; + /// + /// let oldest_people_first = people + /// .into_iter() + /// .sorted_by_cached_key(|x| -x.1) + /// .map(|(person, _age)| person); + /// + /// itertools::assert_equal(oldest_people_first, + /// vec!["Jill", "Jack", "Jane", "John"]); + /// ``` + /// ``` + #[cfg(feature = "use_alloc")] + fn sorted_by_cached_key<K, F>(self, f: F) -> VecIntoIter<Self::Item> + where + Self: Sized, + K: Ord, + F: FnMut(&Self::Item) -> K, + { + let mut v = Vec::from_iter(self); + v.sort_by_cached_key(f); + v.into_iter() + } + /// Sort the k smallest elements into a new iterator, in ascending order. /// /// **Note:** This consumes the entire iterator, and returns the result @@ -2770,6 +2810,8 @@ pub trait Itertools : Iterator { /// Return a `HashMap` of keys mapped to `Vec`s of values. Keys and values /// are taken from `(Key, Value)` tuple pairs yielded by the input iterator. /// + /// Essentially a shorthand for `.into_grouping_map().collect::<Vec<_>>()`. + /// /// ``` /// use itertools::Itertools; /// @@ -2791,8 +2833,8 @@ pub trait Itertools : Iterator { /// Return an `Iterator` on a `HashMap`. Keys mapped to `Vec`s of values. The key is specified /// in the closure. - /// Different to `into_group_map_by` because the key is still present. It is also more general. - /// You can also fold the `group_map`. + /// + /// Essentially a shorthand for `.into_grouping_map_by(f).collect::<Vec<_>>()`. /// /// ``` /// use itertools::Itertools; @@ -3401,6 +3443,33 @@ pub trait Itertools : Iterator { { self.map(f).counts() } + + /// Converts an iterator of tuples into a tuple of containers. + /// + /// `unzip()` consumes an entire iterator of n-ary tuples, producing `n` collections, one for each + /// column. + /// + /// This function is, in some sense, the opposite of [`multizip`]. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let inputs = vec![(1, 2, 3), (4, 5, 6), (7, 8, 9)]; + /// + /// let (a, b, c): (Vec<_>, Vec<_>, Vec<_>) = inputs + /// .into_iter() + /// .multiunzip(); + /// + /// assert_eq!(a, vec![1, 4, 7]); + /// assert_eq!(b, vec![2, 5, 8]); + /// assert_eq!(c, vec![3, 6, 9]); + /// ``` + fn multiunzip<FromI>(self) -> FromI + where + Self: Sized + MultiUnzip<FromI>, + { + MultiUnzip::multiunzip(self) + } } impl<T: ?Sized> Itertools for T where T: Iterator { } diff --git a/src/multipeek_impl.rs b/src/multipeek_impl.rs index 93b0227..5917681 100644 --- a/src/multipeek_impl.rs +++ b/src/multipeek_impl.rs @@ -38,6 +38,7 @@ impl<I: Iterator> MultiPeek<I> { /// 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]) diff --git a/src/pad_tail.rs b/src/pad_tail.rs index 03867cb..de57ee4 100644 --- a/src/pad_tail.rs +++ b/src/pad_tail.rs @@ -16,6 +16,13 @@ pub struct PadUsing<I, F> { filler: F, } +impl<I, F> std::fmt::Debug for PadUsing<I, F> +where + I: std::fmt::Debug, +{ + debug_fmt_fields!(PadUsing, iter, min, pos); +} + /// Create a new **PadUsing** iterator. pub fn pad_using<I, F>(iter: I, min: usize, filler: F) -> PadUsing<I, F> where I: Iterator, diff --git a/src/peeking_take_while.rs b/src/peeking_take_while.rs index f9f2134..cd0945a 100644 --- a/src/peeking_take_while.rs +++ b/src/peeking_take_while.rs @@ -83,6 +83,13 @@ pub struct PeekingTakeWhile<'a, I: 'a, F> f: F, } +impl<'a, I: 'a, F> std::fmt::Debug for PeekingTakeWhile<'a, I, F> +where + I: Iterator + std::fmt::Debug, +{ + debug_fmt_fields!(PeekingTakeWhile, iter); +} + /// Create a PeekingTakeWhile pub fn peeking_take_while<I, F>(iter: &mut I, f: F) -> PeekingTakeWhile<I, F> where I: Iterator, diff --git a/src/process_results_impl.rs b/src/process_results_impl.rs index 9da108b..44308f3 100644 --- a/src/process_results_impl.rs +++ b/src/process_results_impl.rs @@ -30,6 +30,23 @@ impl<'a, I, T, E> Iterator for ProcessResults<'a, I, E> fn size_hint(&self) -> (usize, Option<usize>) { (0, self.iter.size_hint().1) } + + fn fold<B, F>(mut self, init: B, mut f: F) -> B + where + Self: Sized, + F: FnMut(B, Self::Item) -> B, + { + let error = self.error; + self.iter + .try_fold(init, |acc, opt| match opt { + Ok(x) => Ok(f(acc, x)), + Err(e) => { + *error = Err(e); + Err(acc) + } + }) + .unwrap_or_else(|e| e) + } } /// “Lift” a function of the values of an iterator so that it can process diff --git a/src/tuple_impl.rs b/src/tuple_impl.rs index ca8b97c..d914e03 100644 --- a/src/tuple_impl.rs +++ b/src/tuple_impl.rs @@ -11,7 +11,7 @@ use std::marker::PhantomData; // hiding the implementation details of `TupleCollect`. // See https://github.com/rust-itertools/itertools/issues/387 -/// Implemented for homogeneous tuples of size up to 4. +/// Implemented for homogeneous tuples of size up to 12. pub trait HomogeneousTuple : TupleCollect {} @@ -77,7 +77,7 @@ impl<T> ExactSizeIterator for TupleBuffer<T> /// An iterator that groups the items in tuples of a specific size. /// /// See [`.tuples()`](crate::Itertools::tuples) for more information. -#[derive(Clone)] +#[derive(Clone, Debug)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct Tuples<I, T> where I: Iterator<Item = T::Item>, diff --git a/src/unziptuple.rs b/src/unziptuple.rs new file mode 100644 index 0000000..f468f05 --- /dev/null +++ b/src/unziptuple.rs @@ -0,0 +1,80 @@ +/// Converts an iterator of tuples into a tuple of containers. +/// +/// `unzip()` consumes an entire iterator of n-ary tuples, producing `n` collections, one for each +/// column. +/// +/// This function is, in some sense, the opposite of [`multizip`]. +/// +/// ``` +/// use itertools::multiunzip; +/// +/// let inputs = vec![(1, 2, 3), (4, 5, 6), (7, 8, 9)]; +/// +/// let (a, b, c): (Vec<_>, Vec<_>, Vec<_>) = multiunzip(inputs); +/// +/// assert_eq!(a, vec![1, 4, 7]); +/// assert_eq!(b, vec![2, 5, 8]); +/// assert_eq!(c, vec![3, 6, 9]); +/// ``` +/// +/// [`multizip`]: crate::multizip +pub fn multiunzip<FromI, I>(i: I) -> FromI +where + I: IntoIterator, + I::IntoIter: MultiUnzip<FromI>, +{ + i.into_iter().multiunzip() +} + +/// An iterator that can be unzipped into multiple collections. +/// +/// See [`.multiunzip()`](crate::Itertools::multiunzip) for more information. +pub trait MultiUnzip<FromI>: Iterator { + /// Unzip this iterator into multiple collections. + fn multiunzip(self) -> FromI; +} + +macro_rules! impl_unzip_iter { + ($($T:ident => $FromT:ident),*) => ( + #[allow(non_snake_case)] + impl<IT: Iterator<Item = ($($T,)*)>, $($T, $FromT: Default + Extend<$T>),* > MultiUnzip<($($FromT,)*)> for IT { + fn multiunzip(self) -> ($($FromT,)*) { + // This implementation mirrors the logic of Iterator::unzip as close as possible. + // Unfortunately a lot of the used api there is still unstable represented by + // the commented out parts that follow. + // + // https://doc.rust-lang.org/src/core/iter/traits/iterator.rs.html#2816-2844 + + let mut res = ($($FromT::default(),)*); + let ($($FromT,)*) = &mut res; + + // Still unstable #72631 + // let (lower_bound, _) = self.size_hint(); + // if lower_bound > 0 { + // $($FromT.extend_reserve(lower_bound);)* + // } + + self.fold((), |(), ($($T,)*)| { + // Still unstable #72631 + // $( $FromT.extend_one($T); )* + $( $FromT.extend(std::iter::once($T)); )* + }); + res + } + } + ); +} + +impl_unzip_iter!(); +impl_unzip_iter!(A => FromA); +impl_unzip_iter!(A => FromA, B => FromB); +impl_unzip_iter!(A => FromA, B => FromB, C => FromC); +impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD); +impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE); +impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF); +impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG); +impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG, H => FromH); +impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG, H => FromH, I => FromI); +impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG, H => FromH, I => FromI, J => FromJ); +impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG, H => FromH, I => FromI, J => FromJ, K => FromK); +impl_unzip_iter!(A => FromA, B => FromB, C => FromC, D => FromD, E => FromE, F => FromF, G => FromG, H => FromH, I => FromI, J => FromJ, K => FromK, L => FromL); diff --git a/tests/fold_specialization.rs b/tests/fold_specialization.rs deleted file mode 100644 index a984b40..0000000 --- a/tests/fold_specialization.rs +++ /dev/null @@ -1,13 +0,0 @@ -use itertools::Itertools; - -#[test] -fn specialization_intersperse() { - let mut iter = (1..2).intersperse(0); - iter.clone().for_each(|x| assert_eq!(Some(x), iter.next())); - - let mut iter = (1..3).intersperse(0); - iter.clone().for_each(|x| assert_eq!(Some(x), iter.next())); - - let mut iter = (1..4).intersperse(0); - iter.clone().for_each(|x| assert_eq!(Some(x), iter.next())); -} diff --git a/tests/specializations.rs b/tests/specializations.rs index bc337c2..199cf56 100644 --- a/tests/specializations.rs +++ b/tests/specializations.rs @@ -15,16 +15,16 @@ where } } -fn check_specialized<'a, V, IterItem, Iter, F>(iterator: &Iter, mapper: F) -where - V: Eq + Debug, - Iter: Iterator<Item = IterItem> + Clone + 'a, - F: Fn(Box<dyn Iterator<Item = IterItem> + 'a>) -> V, -{ - assert_eq!( - mapper(Box::new(Unspecialized(iterator.clone()))), - mapper(Box::new(iterator.clone())) - ) +macro_rules! check_specialized { + ($src:expr, |$it:pat| $closure:expr) => { + let $it = $src.clone(); + let v1 = $closure; + + let $it = Unspecialized($src.clone()); + let v2 = $closure; + + assert_eq!(v1, v2); + } } fn test_specializations<IterItem, Iter>( @@ -33,10 +33,10 @@ fn test_specializations<IterItem, Iter>( IterItem: Eq + Debug + Clone, Iter: Iterator<Item = IterItem> + Clone, { - check_specialized(it, |i| i.count()); - check_specialized(it, |i| i.last()); - check_specialized(it, |i| i.collect::<Vec<_>>()); - check_specialized(it, |i| { + check_specialized!(it, |i| i.count()); + check_specialized!(it, |i| i.last()); + check_specialized!(it, |i| i.collect::<Vec<_>>()); + check_specialized!(it, |i| { let mut parameters_from_fold = vec![]; let fold_result = i.fold(vec![], |mut acc, v: IterItem| { parameters_from_fold.push((acc.clone(), v.clone())); @@ -45,7 +45,7 @@ fn test_specializations<IterItem, Iter>( }); (parameters_from_fold, fold_result) }); - check_specialized(it, |mut i| { + check_specialized!(it, |mut i| { let mut parameters_from_all = vec![]; let first = i.next(); let all_result = i.all(|x| { @@ -56,7 +56,7 @@ fn test_specializations<IterItem, Iter>( }); let size = it.clone().count(); for n in 0..size + 2 { - check_specialized(it, |mut i| i.nth(n)); + check_specialized!(it, |mut i| i.nth(n)); } // size_hint is a bit harder to check let mut it_sh = it.clone(); @@ -73,6 +73,12 @@ fn test_specializations<IterItem, Iter>( } quickcheck! { + fn intersperse(v: Vec<u8>) -> () { + test_specializations(&v.into_iter().intersperse(0)); + } +} + +quickcheck! { fn put_back_qc(test_vec: Vec<i32>) -> () { test_specializations(&itertools::put_back(test_vec.iter())); let mut pb = itertools::put_back(test_vec.into_iter()); @@ -98,3 +104,50 @@ quickcheck! { test_specializations(&v.into_iter().map_ok(|u| u.checked_add(1))); } } + +quickcheck! { + fn process_results(v: Vec<Result<u8, u8>>) -> () { + helper(v.iter().copied()); + helper(v.iter().copied().filter(Result::is_ok)); + + fn helper(it: impl Iterator<Item = Result<u8, u8>> + Clone) { + macro_rules! check_results_specialized { + ($src:expr, |$it:pat| $closure:expr) => { + assert_eq!( + itertools::process_results($src.clone(), |$it| $closure), + itertools::process_results($src.clone(), |i| { + let $it = Unspecialized(i); + $closure + }), + ) + } + } + + check_results_specialized!(it, |i| i.count()); + check_results_specialized!(it, |i| i.last()); + check_results_specialized!(it, |i| i.collect::<Vec<_>>()); + check_results_specialized!(it, |i| { + let mut parameters_from_fold = vec![]; + let fold_result = i.fold(vec![], |mut acc, v| { + parameters_from_fold.push((acc.clone(), v.clone())); + acc.push(v); + acc + }); + (parameters_from_fold, fold_result) + }); + check_results_specialized!(it, |mut i| { + let mut parameters_from_all = vec![]; + let first = i.next(); + let all_result = i.all(|x| { + parameters_from_all.push(x.clone()); + Some(x)==first + }); + (parameters_from_all, all_result) + }); + let size = it.clone().count(); + for n in 0..size + 2 { + check_results_specialized!(it, |mut i| i.nth(n)); + } + } + } +} diff --git a/tests/test_core.rs b/tests/test_core.rs index bcdca0e..a7b7449 100644 --- a/tests/test_core.rs +++ b/tests/test_core.rs @@ -9,6 +9,8 @@ use core::iter; use itertools as it; use crate::it::Itertools; use crate::it::interleave; +use crate::it::intersperse; +use crate::it::intersperse_with; use crate::it::multizip; use crate::it::free::put_back; use crate::it::iproduct; @@ -136,6 +138,23 @@ fn test_interleave() { it::assert_equal(it, rs.iter()); } +#[test] +fn test_intersperse() { + let xs = [1u8, 2, 3]; + let ys = [1u8, 0, 2, 0, 3]; + let it = intersperse(&xs, &0); + it::assert_equal(it, ys.iter()); +} + +#[test] +fn test_intersperse_with() { + let xs = [1u8, 2, 3]; + let ys = [1u8, 10, 2, 10, 3]; + let i = 10; + let it = intersperse_with(&xs, || &i); + it::assert_equal(it, ys.iter()); +} + #[allow(deprecated)] #[test] fn foreach() { diff --git a/tests/test_std.rs b/tests/test_std.rs index 7cda9b5..2049d15 100644 --- a/tests/test_std.rs +++ b/tests/test_std.rs @@ -84,6 +84,13 @@ fn duplicates() { it::assert_equal(ys.iter(), xs.iter().rev().duplicates().rev()); let ys_rev = [1, 0]; it::assert_equal(ys_rev.iter(), xs.iter().duplicates().rev()); + + let xs = vec![0, 1, 2, 1, 2]; + let ys = vec![1, 2]; + assert_eq!(ys, xs.iter().duplicates().cloned().collect_vec()); + assert_eq!(ys, xs.iter().rev().duplicates().rev().cloned().collect_vec()); + let ys_rev = vec![2, 1]; + assert_eq!(ys_rev, xs.iter().duplicates().rev().cloned().collect_vec()); } #[test] @@ -504,6 +511,30 @@ fn sorted_by_key() { } #[test] +fn sorted_by_cached_key() { + // Track calls to key function + let mut ncalls = 0; + + let sorted = [3, 4, 1, 2].iter().cloned().sorted_by_cached_key(|&x| { + ncalls += 1; + x.to_string() + }); + it::assert_equal(sorted, vec![1, 2, 3, 4]); + // Check key function called once per element + assert_eq!(ncalls, 4); + + let mut ncalls = 0; + + let sorted = (0..5).sorted_by_cached_key(|&x| { + ncalls += 1; + -x + }); + it::assert_equal(sorted, vec![4, 3, 2, 1, 0]); + // Check key function called once per element + assert_eq!(ncalls, 5); +} + +#[test] fn test_multipeek() { let nums = vec![1u8,2,3,4,5]; @@ -1080,3 +1111,12 @@ fn exactly_one_question_mark_return() -> Result<(), ExactlyOneError<std::slice:: [].iter().exactly_one()?; Ok(()) } + +#[test] +fn multiunzip() { + let (a, b, c): (Vec<_>, Vec<_>, Vec<_>) = [(0, 1, 2), (3, 4, 5), (6, 7, 8)].iter().cloned().multiunzip(); + assert_eq!((a, b, c), (vec![0, 3, 6], vec![1, 4, 7], vec![2, 5, 8])); + let (): () = [(), (), ()].iter().cloned().multiunzip(); + let t: (Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>, Vec<_>) = [(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)].iter().cloned().multiunzip(); + assert_eq!(t, (vec![0], vec![1], vec![2], vec![3], vec![4], vec![5], vec![6], vec![7], vec![8], vec![9], vec![10], vec![11])); +}
\ No newline at end of file |