зеркало из https://github.com/mozilla/gecko-dev.git
No bug - Revendor rust dependencies
This commit is contained in:
Родитель
68043719a2
Коммит
f5b5dab005
|
@ -1 +1 @@
|
|||
{"files":{".travis.yml":"5edd53edc70be60b7c6797204b540780d0ca2550d5b073eb99ee5255f3059682","Cargo.toml":"ba266ea33473c807fffe8e176bd1ec4297f5eb0b5fb583387f504ceb6f3fa896","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"7576269ea71f767b99297934c0b2367532690f8c4badc695edf8e04ab6a1e545","Makefile":"13f1c5b88a7b946b5813f7231df2933b6b19b223e9e2d3fa63ad681192f984b5","README.rst":"2764f3dec0b6bc8270fdad6b765354b99da9996dacb58e2c2f706621d223c5ce","benches/bench1.rs":"982de314e181dc6d4da9530d37b72c4f8d8a02e3e204a90f91ea72bb22937cf7","benches/extra/mod.rs":"4c5b03e74fc5b02383500c9da9fd6550262706ee569d70d085700f6d0b5749ba","benches/extra/zipslices.rs":"108dd488de366b2d83fb6bcc603ecbf9a017e165ac19d03440074fa244af3fb2","benches/tuple_combinations.rs":"8c14e9341d92e5cfd5f9a067d11088b37b003e82635d1ab3a8e5290e3ef83eed","benches/tuples.rs":"412a952f08bb03695952d5cfd57949dcf28be8b99e3c6653994bdb8af9654653","custom.css":"03d2316d325a09f03f0fae54d24b64f784518a8249432edbd60e01436be900d5","examples/iris.data":"596ffd580471ca4d4880f8e439c7281f3b50d8249a5960353cb200b1490f63a0","examples/iris.rs":"74387bb7e81d4b975f796aa855788ff5b8e3c9ff69c42898ecf76c4a74e8504b","src/adaptors/mod.rs":"af2c2b96fb4d1c6024af78d2faf766e9870180622e1c5b603d1799564fffaa34","src/adaptors/multipeek.rs":"2b2f3b796b5664e3561a9ffabd16ea2971020cea13e1fb2ce5fd6e995934b4ab","src/cons_tuples_impl.rs":"371d58a3e0aaa9552219450863afd685b75fb7d4f2be69e14138e2c5d602591c","src/diff.rs":"921e2b867d7b32ffedc72a5eb780811322d14d1e0883a608b9028a2afcad0df2","src/format.rs":"412fbe02f12311c6fbcec1044f57ad6991783f5a3f323b9c391accfe4915106f","src/free.rs":"2c3e853dda297f4227cd7ecd37402a99c36169884ffbdfe823d296b0c8d16d33","src/groupbylazy.rs":"62957f8b15dd3083ac7077edc357a0bc0954828f467b60697a8801de57028d2d","src/impl_macros.rs":"eb0bb3f70ec1bcaffa6110ae4134c777951ed1e5f48d8c811dbf0a597dc48faa","src/intersperse.rs":"8338a5b61ff5d2eb306ef7142578406f3ae4e4c7b2a8adcaa293a07c2299735b","src/kmerge_impl.rs":"e7902ccf6b811417e4dd9314964944beb08c2908e76396ff977227a7a350a16f","src/lib.rs":"adb75487a37790bd086880b44fc65eff990ea3d7897ae323adf6967d87be8cf7","src/minmax.rs":"4668a7f824fbc133599f43ffb6f7283e5bd603e07df2d8176abc6f25d6af9db0","src/pad_tail.rs":"2b4c8961a18bc9685cfa4ac674b77f1f313e953e1398d08d681255b9e5f60ad7","src/peeking_take_while.rs":"e44361e2793db239d367ae3d052376b4fbc9dec472e7f89f10347cdd1e197de4","src/rciter_impl.rs":"9ecde85b56122db166ffd0a6cc8e9d819b3a2c4322c880ccd9bf0b77f5a8c985","src/repeatn.rs":"e60885e395eb8a348248fe8c0d05c325c74056198cc0242d25530c93520f9b25","src/size_hint.rs":"c624ab3ff04836372d98cbd597be134da283280be5378d06747759febe5c2ee7","src/sources.rs":"d6d4ac8980ede2975363a27431b6584af43cc495c10a21d6dfe2dcee6393716d","src/tee.rs":"86b1da0697360091ae5de53a64cd8efb927b88c41c7fff5dec5814702c5bac31","src/tuple_impl.rs":"767624e7c7db930fabf22542a3b48926e48a0b485bdbf03cfa131286bc6988de","src/with_position.rs":"8af04f26d9b89a2597fc10ad52c0feb61cb852dbf988b93d397f3300a6e70965","src/zip_eq_impl.rs":"95e493deeadd640751f5c49f55008bd31218978f38396967bc4a356f6f11d209","src/zip_longest.rs":"e463c58d5feebe5a0ed7964705ffedc6ec9a89ca2567a374cc8ceaf206249d5a","src/ziptuple.rs":"463f102add23ffa0702ec5ef110aae6c132267151fad66566b724fff7ebfa458","tests/peeking_take_while.rs":"a2ae6474e09620a47bb8a6e3c62929261e72c52881370adb2d22e89aa9e9aec8","tests/quick.rs":"a4f9faafbf2ee7fa76048634c84422c24e31a8c65489964017d1b100f9e1bbc0","tests/tests.rs":"a1fa1f0e5e70e6ef78ffce2e145422941e5aea18a1a5b66ef7365e7962511368","tests/tuples.rs":"f8c8892c3c44dde0910eaf26f1756ddc62e264498245e5b0070a6912dc8c101c","tests/zip.rs":"00749b70157da84dc3f0681b5fb8aaabfe412ab930da5fbaa3b4571f4c662fe9"},"package":"4833d6978da405305126af4ac88569b5d71ff758581ce5a987dbfa3755f694fc"}
|
||||
{"files":{".travis.yml":"c3dc3f05ae5a558675996efeec1bcdc1f51be7abe519f2e3489b4c3974af851f","Cargo.toml":"9aee0be6b22b8323448a4af38d5b923a0fe47245a3e01573d0ad51fa1c81ffa2","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"7576269ea71f767b99297934c0b2367532690f8c4badc695edf8e04ab6a1e545","Makefile":"13f1c5b88a7b946b5813f7231df2933b6b19b223e9e2d3fa63ad681192f984b5","README.rst":"5d457160aa59d3e9ea1c7ad92e1f450ac43c867a72b854baa2e536152c1335c1","benches/bench1.rs":"695e4b00addf2e8e44a09d85a588cb333c97109829e59739bdab01e7342d47b5","benches/extra/mod.rs":"4c5b03e74fc5b02383500c9da9fd6550262706ee569d70d085700f6d0b5749ba","benches/extra/zipslices.rs":"108dd488de366b2d83fb6bcc603ecbf9a017e165ac19d03440074fa244af3fb2","benches/tuple_combinations.rs":"8c14e9341d92e5cfd5f9a067d11088b37b003e82635d1ab3a8e5290e3ef83eed","benches/tuples.rs":"412a952f08bb03695952d5cfd57949dcf28be8b99e3c6653994bdb8af9654653","custom.css":"03d2316d325a09f03f0fae54d24b64f784518a8249432edbd60e01436be900d5","examples/iris.data":"596ffd580471ca4d4880f8e439c7281f3b50d8249a5960353cb200b1490f63a0","examples/iris.rs":"3996ca0a62762aec2b102f0f4244fe90d4b4354286d68d80cdc40e35f4352ba3","src/adaptors/mod.rs":"63da1c2f3cf9a33cbfd19721c955b3c2f052b4f5cf8c609dcb37b2698f30720c","src/adaptors/multi_product.rs":"e51e36b6f6ff3bb820c3544f7f80f700b0d1a2c4c7a51cff4e39b78750341d3c","src/combinations.rs":"a9a3fc78eb5c9f3933ff60275a635d0c81f4864a73515dc052aeb2add4ad4909","src/concat_impl.rs":"276339b00588f54c25f8ffbe0ae3c0031f7e52fb53c6578554a0bde1681b58a5","src/cons_tuples_impl.rs":"24511088aa946fec4ed4c46cf2d64c0928eea9e8bc410d2fbc3e344b6c63074d","src/diff.rs":"921e2b867d7b32ffedc72a5eb780811322d14d1e0883a608b9028a2afcad0df2","src/either_or_both.rs":"4739b8644fa932b7992e565be6a6bc64dff9f93e345c52f90b3e1f8a67e6d18e","src/format.rs":"412fbe02f12311c6fbcec1044f57ad6991783f5a3f323b9c391accfe4915106f","src/free.rs":"ced78d79c0c78398ac53bf628e77ae84f214972d0dad0507b6687c2f88873aaa","src/groupbylazy.rs":"4de00767e6ec2aa27419bcf7a88a2e45f41a5e73103227d99e8b2d9d147dcf6b","src/impl_macros.rs":"eb0bb3f70ec1bcaffa6110ae4134c777951ed1e5f48d8c811dbf0a597dc48faa","src/intersperse.rs":"8f4053a203068b0d7ddab480d454910b508bec227bdf2c0d66f6f7ed7e56ce96","src/kmerge_impl.rs":"a15ed167c535f2a5700b1efd021adbc0b793a001773cd1f6648a5068b8a32245","src/lib.rs":"729a0718daad88348d32d623255a90a7656bf1a27301817108e8829debc18548","src/merge_join.rs":"d6952726da870aab6ee0607822384aad30cc4ff0ffc7223c9a707b125b1b8b5b","src/minmax.rs":"4668a7f824fbc133599f43ffb6f7283e5bd603e07df2d8176abc6f25d6af9db0","src/multipeek_impl.rs":"ebe9544d94d0bf7200f7625241a3b5a291b7b564091a08cad40ff08b51f1b1bf","src/pad_tail.rs":"078615a2892f8c6db665074cf6f1be1bef4cf5ee418bc174edcfd4dc703e163f","src/peeking_take_while.rs":"6aea3bb40fb480e9f3695ce2a7a3a2e2346d437ca846d20e6bb3c09beb0934f4","src/process_results_impl.rs":"214608f9a67a5a55b3c9c8e136355f78d0f8a9e0000c4523faaa44a05f070961","src/put_back_n_impl.rs":"d35858184c525372b22d14d42cdf63726cf0fd50f5bd42ec7a82d55a8e180e9f","src/rciter_impl.rs":"449262cb5c2d1e9affe0b4020f1c28aa316a3718afe094667b79bbff1557d5e6","src/repeatn.rs":"5192191b87efe68f60353f742114da33e4d490a302671036f1b262d1f0a004c1","src/size_hint.rs":"c1d35b422a696cf3d63e7c90d8f9fdf01a304cf8156e914287c4ef48fea62dd3","src/sources.rs":"9d7eb4dbd87f659d04b4980238c5fc72b71472e16861d17a32cab9b77a3df06a","src/tee.rs":"f8cf7fb07506b3a1f2d59a1ec5a6b858534af90df1dad745f38761e73eab6baf","src/tuple_impl.rs":"0a26201089b3ee6546e174fc7f89aadde415f4eb201160f3d9b42fe857d03946","src/unique_impl.rs":"601a231786f61b503e55c22def380fa8b026b615125bcf90356587c761892bc8","src/with_position.rs":"d922f045f6fa090a431be928f3221c6dc37ac6f9bb54461b3b84f99a7e91244a","src/zip_eq_impl.rs":"95e493deeadd640751f5c49f55008bd31218978f38396967bc4a356f6f11d209","src/zip_longest.rs":"8ab899b3bf0295cbc076770b003499b91e93310f02ce05d15b91d39aa218bdd2","src/ziptuple.rs":"bd75c3f9493a7b9f949f27310c2f0a7a3504baac988fd7399dd252389be1b39d","tests/merge_join.rs":"546eaffae40010f15a7bcf95bc53f5e9b67424c5b93df6ffb0aaa1e48e8b90c0","tests/peeking_take_while.rs":"a2ae6474e09620a47bb8a6e3c62929261e72c52881370adb2d22e89aa9e9aec8","tests/quick.rs":"f80a6d47fc233fd23efaa0a3358419b403869d273cc0c82049e09114d0e0bcb5","tests/test_core.rs":"21037f9af8b5a69ebc02636fe2128f4d36a46ca9241c6bee74f32f6446afcaa8","tests/test_std.rs":"756db4247a7dae7ae599ab2796d6e1e2fc740102cc2e57a73bec0a2ead3335e8","tests/tuples.rs":"5323d15a7abf6545b2655167d3206b6cf6a947e9409a244ea6a8cf4ad8ceac64","tests/zip.rs":"fe213d70c4fa114cb4d1930a6b971f4af617a239041ddb87e6b5a9bbe62261b8"},"package":"b07332223953b5051bceb67e8c4700aa65291535568e1f12408c43c4a42c0394"}
|
|
@ -2,7 +2,7 @@ language: rust
|
|||
sudo: false
|
||||
matrix:
|
||||
include:
|
||||
- rust: 1.11.0
|
||||
- rust: 1.12.0
|
||||
- rust: stable
|
||||
- rust: beta
|
||||
- rust: nightly
|
||||
|
@ -13,6 +13,7 @@ branches:
|
|||
- master
|
||||
script:
|
||||
- |
|
||||
cargo build --verbose --no-default-features &&
|
||||
cargo build --verbose --features "$FEATURES" &&
|
||||
cargo test --verbose --features "$FEATURES" &&
|
||||
([ "$BENCH" != 1 ] || cargo bench --verbose --features "$FEATURES")
|
||||
|
|
|
@ -1,35 +1,43 @@
|
|||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g. crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
name = "itertools"
|
||||
version = "0.5.10"
|
||||
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/bluss/rust-itertools"
|
||||
documentation = "https://docs.rs/itertools/"
|
||||
version = "0.7.6"
|
||||
authors = ["bluss"]
|
||||
|
||||
description = "Extra iterator adaptors, iterator methods, free functions, and macros."
|
||||
|
||||
documentation = "https://docs.rs/itertools/"
|
||||
keywords = ["iterator", "data-structure", "zip", "product", "group-by"]
|
||||
categories = ["algorithms", "rust-patterns"]
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/bluss/rust-itertools"
|
||||
[package.metadata.release]
|
||||
no-dev-version = true
|
||||
[profile.bench]
|
||||
debug = true
|
||||
|
||||
[lib]
|
||||
bench = false
|
||||
test = false
|
||||
|
||||
[dependencies]
|
||||
either = { version = "1.0", default-features = false }
|
||||
|
||||
[dev-dependencies.quickcheck]
|
||||
version = "0.4"
|
||||
bench = false
|
||||
[dependencies.either]
|
||||
version = "1.0"
|
||||
default-features = false
|
||||
|
||||
[dev-dependencies.permutohedron]
|
||||
version = "0.2"
|
||||
|
||||
[dev-dependencies.quickcheck]
|
||||
version = "0.5"
|
||||
default-features = false
|
||||
|
||||
[features]
|
||||
|
||||
[profile]
|
||||
bench = { debug = true }
|
||||
|
||||
[package.metadata.release]
|
||||
no-dev-version = true
|
||||
default = ["use_std"]
|
||||
use_std = []
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
Itertools
|
||||
=========
|
||||
|
||||
Extra iterator adaptors, functions and macros. Requires Rust 1.11 or later.
|
||||
Extra iterator adaptors, functions and macros.
|
||||
|
||||
Please read the `API documentation here`__
|
||||
|
||||
|
@ -21,7 +21,7 @@ How to use with cargo:
|
|||
.. code:: toml
|
||||
|
||||
[dependencies]
|
||||
itertools = "0.5.9"
|
||||
itertools = "0.7.3"
|
||||
|
||||
How to use in your crate:
|
||||
|
||||
|
@ -41,6 +41,100 @@ How to contribute:
|
|||
Recent Changes
|
||||
--------------
|
||||
|
||||
- 0.7.6
|
||||
|
||||
- Add new adaptor ``.multi_cartesian_product()`` which is an n-ary product
|
||||
iterator by @tobz1000
|
||||
- Add new method ``.sorted_by_key()`` by @Xion
|
||||
- Provide simpler and faster ``.count()`` for ``.unique()`` and ``.unique_by()``
|
||||
|
||||
- 0.7.5
|
||||
|
||||
- ``.multipeek()`` now implements ``PeekingNext``, by @nicopap.
|
||||
|
||||
- 0.7.4
|
||||
|
||||
- Add new adaptor ``.update()`` by @lucasem; this adaptor is used
|
||||
to modify an element before passing it on in an iterator chain.
|
||||
|
||||
- 0.7.3
|
||||
|
||||
- Add new method ``.collect_tuple()`` by @matklad; it makes a tuple out of
|
||||
the iterator's elements if the number of them matches **exactly**.
|
||||
- Implement ``fold`` and ``collect`` for ``.map_results()`` which means
|
||||
it reuses the code of the standard ``.map()`` for these methods.
|
||||
|
||||
- 0.7.2
|
||||
|
||||
- Add new adaptor ``.merge_join_by`` by @srijs; a heterogeneous merge join
|
||||
for two ordered sequences.
|
||||
|
||||
- 0.7.1
|
||||
|
||||
- Iterator adaptors and iterators in itertools now use the same ``must_use``
|
||||
reminder that the standard library adaptors do, by @matematikaedit and @bluss
|
||||
*“iterator adaptors are lazy and do nothing unless consumed”*.
|
||||
|
||||
- 0.7.0
|
||||
|
||||
- Faster ``izip!()`` by @krdln
|
||||
|
||||
- ``izip!()`` is now a wrapper for repeated regular ``.zip()`` and
|
||||
a single ``.map()``. This means it optimizes as well as the standard
|
||||
library ``.zip()`` it uses.
|
||||
**Note:** ``multizip`` and ``izip!()`` are now different! The former
|
||||
has a named type but the latter optimizes better.
|
||||
|
||||
- Faster ``.unique()``
|
||||
|
||||
- ``no_std`` support, which is opt-in!
|
||||
|
||||
- Many lovable features are still there without std, like ``izip!()``
|
||||
or ``.format()`` or ``.merge()``, but not those that use collections.
|
||||
|
||||
- Trait bounds were required up front instead of just on the type:
|
||||
``group_by``'s ``PartialEq`` by @Phlosioneer and ``repeat_call``'s
|
||||
``FnMut``.
|
||||
- Removed deprecated constructor ``Zip::new`` — use ``izip!()`` or ``multizip()``
|
||||
|
||||
- 0.6.5
|
||||
|
||||
- Fix bug in ``.cartesian_product()``'s fold (which only was visible for
|
||||
unfused iterators).
|
||||
|
||||
- 0.6.4
|
||||
|
||||
- Add specific ``fold`` implementations for ``.cartesian_product()`` and
|
||||
``cons_tuples()``, which improves their performance in fold, foreach, and
|
||||
iterator consumers derived from them.
|
||||
|
||||
- 0.6.3
|
||||
|
||||
- Add iterator adaptor ``.positions(predicate)`` by @tmccombs
|
||||
|
||||
- 0.6.2
|
||||
|
||||
- Add function ``process_results`` which can “lift” a function of the regular
|
||||
values of an iterator so that it can process the ``Ok`` values from an
|
||||
iterator of ``Results`` instead, by @shepmaster
|
||||
- Add iterator method ``.concat()`` which combines all iterator elements
|
||||
into a single collection using the ``Extend`` trait, by @srijs
|
||||
|
||||
- 0.6.1
|
||||
|
||||
- Better size hint testing and subsequent size hint bugfixes by @rkarp.
|
||||
Fixes bugs in product, interleave_shortest size hints.
|
||||
- New iterator method ``.all_equal()`` by @phimuemue
|
||||
|
||||
- 0.6.0
|
||||
|
||||
- Deprecated names were removed in favour of their replacements
|
||||
- ``.flatten()`` does not implement double ended iteration anymore
|
||||
- ``.fold_while()`` uses ``&mut self`` and returns ``FoldWhile<T>``, for
|
||||
composability (#168)
|
||||
- ``.foreach()`` and ``.fold1()`` use ``self``, like ``.fold()`` does.
|
||||
- ``.combinations(0)`` now produces a single empty vector. (#174)
|
||||
|
||||
- 0.5.10
|
||||
|
||||
- Add itertools method ``.kmerge_by()`` (and corresponding free function)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#![feature(test)]
|
||||
|
||||
extern crate test;
|
||||
extern crate itertools;
|
||||
#[macro_use] extern crate itertools;
|
||||
|
||||
use test::{black_box};
|
||||
use itertools::Itertools;
|
||||
|
@ -649,3 +649,87 @@ fn step_range_10(b: &mut test::Bencher) {
|
|||
fast_integer_sum(v.clone().step(10))
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn cartesian_product_iterator(b: &mut test::Bencher)
|
||||
{
|
||||
let xs = vec![0; 16];
|
||||
|
||||
b.iter(|| {
|
||||
let mut sum = 0;
|
||||
for (&x, &y, &z) in iproduct!(&xs, &xs, &xs) {
|
||||
sum += x;
|
||||
sum += y;
|
||||
sum += z;
|
||||
}
|
||||
sum
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn cartesian_product_fold(b: &mut test::Bencher)
|
||||
{
|
||||
let xs = vec![0; 16];
|
||||
|
||||
b.iter(|| {
|
||||
let mut sum = 0;
|
||||
iproduct!(&xs, &xs, &xs).fold((), |(), (&x, &y, &z)| {
|
||||
sum += x;
|
||||
sum += y;
|
||||
sum += z;
|
||||
});
|
||||
sum
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn multi_cartesian_product_iterator(b: &mut test::Bencher)
|
||||
{
|
||||
let xs = [vec![0; 16], vec![0; 16], vec![0; 16]];
|
||||
|
||||
b.iter(|| {
|
||||
let mut sum = 0;
|
||||
for x in xs.into_iter().multi_cartesian_product() {
|
||||
sum += x[0];
|
||||
sum += x[1];
|
||||
sum += x[2];
|
||||
}
|
||||
sum
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn multi_cartesian_product_fold(b: &mut test::Bencher)
|
||||
{
|
||||
let xs = [vec![0; 16], vec![0; 16], vec![0; 16]];
|
||||
|
||||
b.iter(|| {
|
||||
let mut sum = 0;
|
||||
xs.into_iter().multi_cartesian_product().fold((), |(), x| {
|
||||
sum += x[0];
|
||||
sum += x[1];
|
||||
sum += x[2];
|
||||
});
|
||||
sum
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn cartesian_product_nested_for(b: &mut test::Bencher)
|
||||
{
|
||||
let xs = vec![0; 16];
|
||||
|
||||
b.iter(|| {
|
||||
let mut sum = 0;
|
||||
for &x in &xs {
|
||||
for &y in &xs {
|
||||
for &z in &xs {
|
||||
sum += x;
|
||||
sum += y;
|
||||
sum += z;
|
||||
}
|
||||
}
|
||||
}
|
||||
sum
|
||||
})
|
||||
}
|
||||
|
|
|
@ -110,11 +110,11 @@ fn main() {
|
|||
// using Itertools::set_from
|
||||
plot.iter_mut().set_from(repeat(' '));
|
||||
|
||||
// using Itertools::minmax_by
|
||||
// using Itertools::minmax
|
||||
let min_max = |data: &[Iris], col| {
|
||||
data.iter()
|
||||
.map(|iris| iris.data[col])
|
||||
.minmax_by(|a, b| f32::partial_cmp(a, b).unwrap())
|
||||
.minmax()
|
||||
.into_option()
|
||||
.expect("Can't find min/max of empty iterator")
|
||||
};
|
||||
|
|
|
@ -4,20 +4,17 @@
|
|||
//! option. This file may not be copied, modified, or distributed
|
||||
//! except according to those terms.
|
||||
|
||||
use std::cmp;
|
||||
mod multi_product;
|
||||
#[cfg(feature = "use_std")]
|
||||
pub use self::multi_product::*;
|
||||
|
||||
use std::fmt;
|
||||
use std::mem::replace;
|
||||
use std::ops::Index;
|
||||
use std::iter::{Fuse, Peekable};
|
||||
use std::collections::HashSet;
|
||||
use std::hash::Hash;
|
||||
use std::iter::{Fuse, Peekable, FromIterator};
|
||||
use std::marker::PhantomData;
|
||||
use size_hint;
|
||||
use fold;
|
||||
|
||||
pub mod multipeek;
|
||||
pub use self::multipeek::MultiPeek;
|
||||
|
||||
macro_rules! clone_fields {
|
||||
($name:ident, $base:expr, $($field:ident),+) => (
|
||||
$name {
|
||||
|
@ -28,7 +25,6 @@ macro_rules! clone_fields {
|
|||
);
|
||||
}
|
||||
|
||||
|
||||
/// An iterator adaptor that alternates elements from two iterators until both
|
||||
/// run out.
|
||||
///
|
||||
|
@ -36,6 +32,7 @@ macro_rules! clone_fields {
|
|||
///
|
||||
/// See [`.interleave()`](../trait.Itertools.html#method.interleave) for more information.
|
||||
#[derive(Clone, Debug)]
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct Interleave<I, J> {
|
||||
a: Fuse<I>,
|
||||
b: Fuse<J>,
|
||||
|
@ -98,6 +95,7 @@ impl<I, J> Iterator for Interleave<I, J>
|
|||
/// See [`.interleave_shortest()`](../trait.Itertools.html#method.interleave_shortest)
|
||||
/// for more information.
|
||||
#[derive(Clone, Debug)]
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct InterleaveShortest<I, J>
|
||||
where I: Iterator,
|
||||
J: Iterator<Item = I::Item>
|
||||
|
@ -147,24 +145,38 @@ impl<I, J> Iterator for InterleaveShortest<I, J>
|
|||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let bound = |a: usize, b: usize| -> Option<usize> {
|
||||
use std::cmp::min;
|
||||
2usize.checked_mul(min(a, b))
|
||||
.and_then(|lhs| lhs.checked_add(if !self.phase && a > b || (self.phase && a < b) { 1 } else { 0 }))
|
||||
let (curr_hint, next_hint) = {
|
||||
let it0_hint = self.it0.size_hint();
|
||||
let it1_hint = self.it1.size_hint();
|
||||
if self.phase {
|
||||
(it1_hint, it0_hint)
|
||||
} else {
|
||||
(it0_hint, it1_hint)
|
||||
}
|
||||
};
|
||||
|
||||
let (l0, u0) = self.it0.size_hint();
|
||||
let (l1, u1) = self.it1.size_hint();
|
||||
let lb = bound(l0, l1).unwrap_or(usize::max_value());
|
||||
let ub = match (u0, u1) {
|
||||
(None, None) => None,
|
||||
(Some(u0), None) => Some(u0 * 2 + self.phase as usize),
|
||||
(None, Some(u1)) => Some(u1 * 2 + !self.phase as usize),
|
||||
(Some(u0), Some(u1)) => Some(cmp::min(u0, u1) * 2 +
|
||||
(u0 > u1 && !self.phase ||
|
||||
(u0 < u1 && self.phase)) as usize),
|
||||
let (curr_lower, curr_upper) = curr_hint;
|
||||
let (next_lower, next_upper) = next_hint;
|
||||
let (combined_lower, combined_upper) =
|
||||
size_hint::mul_scalar(size_hint::min(curr_hint, next_hint), 2);
|
||||
let lower =
|
||||
if curr_lower > next_lower {
|
||||
combined_lower + 1
|
||||
} else {
|
||||
combined_lower
|
||||
};
|
||||
(lb, ub)
|
||||
let upper = {
|
||||
let extra_elem = match (curr_upper, next_upper) {
|
||||
(_, None) => false,
|
||||
(None, Some(_)) => true,
|
||||
(Some(curr_max), Some(next_max)) => curr_max > next_max,
|
||||
};
|
||||
if extra_elem {
|
||||
combined_upper.and_then(|x| x.checked_add(1))
|
||||
} else {
|
||||
combined_upper
|
||||
}
|
||||
};
|
||||
(lower, upper)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -193,16 +205,6 @@ pub fn put_back<I>(iterable: I) -> PutBack<I::IntoIter>
|
|||
impl<I> PutBack<I>
|
||||
where I: Iterator
|
||||
{
|
||||
#[doc(hidden)]
|
||||
#[deprecated(note = "replaced by put_back")]
|
||||
#[inline]
|
||||
pub fn new(it: I) -> Self {
|
||||
PutBack {
|
||||
top: None,
|
||||
iter: it,
|
||||
}
|
||||
}
|
||||
|
||||
/// put back value `value` (builder method)
|
||||
pub fn with_value(mut self, value: I::Item) -> Self {
|
||||
self.put_back(value);
|
||||
|
@ -264,74 +266,6 @@ impl<I> Iterator for PutBack<I>
|
|||
}
|
||||
}
|
||||
|
||||
/// An iterator adaptor that allows putting multiple
|
||||
/// items in front of the iterator.
|
||||
///
|
||||
/// Iterator element type is `I::Item`.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PutBackN<I: Iterator> {
|
||||
top: Vec<I::Item>,
|
||||
iter: I,
|
||||
}
|
||||
|
||||
/// Create an iterator where you can put back multiple values to the front
|
||||
/// of the iteration.
|
||||
///
|
||||
/// Iterator element type is `I::Item`.
|
||||
pub fn put_back_n<I>(iterable: I) -> PutBackN<I::IntoIter>
|
||||
where I: IntoIterator
|
||||
{
|
||||
PutBackN {
|
||||
top: Vec::new(),
|
||||
iter: iterable.into_iter(),
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Iterator> PutBackN<I> {
|
||||
#[doc(hidden)]
|
||||
#[deprecated(note = "replaced by put_back_n")]
|
||||
#[inline]
|
||||
pub fn new(it: I) -> Self {
|
||||
put_back_n(it)
|
||||
}
|
||||
|
||||
/// Puts x in front of the iterator.
|
||||
/// The values are yielded in order of the most recently put back
|
||||
/// values first.
|
||||
///
|
||||
/// ```rust
|
||||
/// use itertools::PutBackN;
|
||||
///
|
||||
/// let mut it = PutBackN::new(1..5);
|
||||
/// it.next();
|
||||
/// it.put_back(1);
|
||||
/// it.put_back(0);
|
||||
///
|
||||
/// assert!(itertools::equal(it, 0..5));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn put_back(&mut self, x: I::Item) {
|
||||
self.top.push(x);
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Iterator> Iterator for PutBackN<I> {
|
||||
type Item = I::Item;
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<I::Item> {
|
||||
if self.top.is_empty() {
|
||||
self.iter.next()
|
||||
} else {
|
||||
self.top.pop()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
size_hint::add_scalar(self.iter.size_hint(), self.top.len())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// An iterator adaptor that iterates over the cartesian product of
|
||||
/// the element sets of two iterators `I` and `J`.
|
||||
|
@ -339,6 +273,7 @@ impl<I: Iterator> Iterator for PutBackN<I> {
|
|||
/// Iterator element type is `(I::Item, J::Item)`.
|
||||
///
|
||||
/// See [`.cartesian_product()`](../trait.Itertools.html#method.cartesian_product) for more information.
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct Product<I, J>
|
||||
where I: Iterator
|
||||
{
|
||||
|
@ -396,12 +331,34 @@ impl<I, J> Iterator for Product<I, J>
|
|||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let has_cur = self.a_cur.is_some() as usize;
|
||||
// Not ExactSizeIterator because size may be larger than usize
|
||||
let (b, _) = self.b.size_hint();
|
||||
let (b_min, b_max) = self.b.size_hint();
|
||||
|
||||
// Compute a * b_orig + b for both lower and upper bound
|
||||
size_hint::add_scalar(
|
||||
size_hint::add(
|
||||
size_hint::mul(self.a.size_hint(), self.b_orig.size_hint()),
|
||||
b * has_cur)
|
||||
(b_min * has_cur, b_max.map(move |x| x * has_cur)))
|
||||
}
|
||||
|
||||
fn fold<Acc, G>(mut self, mut accum: Acc, mut f: G) -> Acc
|
||||
where G: FnMut(Acc, Self::Item) -> Acc,
|
||||
{
|
||||
// use a split loop to handle the loose a_cur as well as avoiding to
|
||||
// clone b_orig at the end.
|
||||
if let Some(mut a) = self.a_cur.take() {
|
||||
let mut b = self.b;
|
||||
loop {
|
||||
accum = b.fold(accum, |acc, elt| f(acc, (a.clone(), elt)));
|
||||
|
||||
// we can only continue iterating a if we had a first element;
|
||||
if let Some(next_a) = self.a.next() {
|
||||
b = self.b_orig.clone();
|
||||
a = next_a;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
accum
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -412,6 +369,7 @@ impl<I, J> Iterator for Product<I, J>
|
|||
///
|
||||
/// See [`.batching()`](../trait.Itertools.html#method.batching) for more information.
|
||||
#[derive(Clone)]
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct Batching<I, F> {
|
||||
f: F,
|
||||
iter: I,
|
||||
|
@ -451,6 +409,7 @@ impl<B, F, I> Iterator for Batching<I, F>
|
|||
///
|
||||
/// See [`.step()`](../trait.Itertools.html#method.step) for more information.
|
||||
#[derive(Clone, Debug)]
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct Step<I> {
|
||||
iter: Fuse<I>,
|
||||
skip: usize,
|
||||
|
@ -564,6 +523,7 @@ impl<I, J> MergeCore<I, J>
|
|||
/// Iterator element type is `I::Item`.
|
||||
///
|
||||
/// See [`.merge()`](../trait.Itertools.html#method.merge_by) for more information.
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct Merge<I, J>
|
||||
where I: Iterator,
|
||||
J: Iterator<Item = I::Item>
|
||||
|
@ -636,6 +596,7 @@ impl<I, J> Iterator for Merge<I, J>
|
|||
/// Iterator element type is `I::Item`.
|
||||
///
|
||||
/// See [`.merge_by()`](../trait.Itertools.html#method.merge_by) for more information.
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct MergeBy<I, J, F>
|
||||
where I: Iterator,
|
||||
J: Iterator<Item = I::Item>
|
||||
|
@ -736,6 +697,7 @@ impl<I> CoalesceCore<I>
|
|||
/// An iterator adaptor that may join together adjacent elements.
|
||||
///
|
||||
/// See [`.coalesce()`](../trait.Itertools.html#method.coalesce) for more information.
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct Coalesce<I, F>
|
||||
where I: Iterator
|
||||
{
|
||||
|
@ -790,6 +752,7 @@ impl<I, F> Iterator for Coalesce<I, F>
|
|||
/// An iterator adaptor that removes repeated duplicates.
|
||||
///
|
||||
/// See [`.dedup()`](../trait.Itertools.html#method.dedup) for more information.
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct Dedup<I>
|
||||
where I: Iterator
|
||||
{
|
||||
|
@ -862,6 +825,7 @@ impl<I> Iterator for Dedup<I>
|
|||
/// to only pick off elements while the predicate returns `true`.
|
||||
///
|
||||
/// See [`.take_while_ref()`](../trait.Itertools.html#method.take_while_ref) for more information.
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct TakeWhileRef<'a, I: 'a, F> {
|
||||
iter: &'a mut I,
|
||||
f: F,
|
||||
|
@ -912,6 +876,7 @@ impl<'a, I, F> Iterator for TakeWhileRef<'a, I, F>
|
|||
///
|
||||
/// See [`.while_some()`](../trait.Itertools.html#method.while_some) for more information.
|
||||
#[derive(Clone, Debug)]
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct WhileSome<I> {
|
||||
iter: I,
|
||||
}
|
||||
|
@ -945,6 +910,7 @@ impl<I, A> Iterator for WhileSome<I>
|
|||
/// See [`.tuple_combinations()`](../trait.Itertools.html#method.tuple_combinations) for more
|
||||
/// information.
|
||||
#[derive(Debug)]
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct TupleCombinations<I, T>
|
||||
where I: Iterator,
|
||||
T: HasCombination<I>
|
||||
|
@ -1068,289 +1034,15 @@ impl_tuple_combination!(Tuple2Combination Tuple1Combination ; A, A, A ; a);
|
|||
impl_tuple_combination!(Tuple3Combination Tuple2Combination ; A, A, A, A ; a b);
|
||||
impl_tuple_combination!(Tuple4Combination Tuple3Combination ; A, A, A, A, A; a b c);
|
||||
|
||||
#[derive(Debug)]
|
||||
struct LazyBuffer<I: Iterator> {
|
||||
it: I,
|
||||
done: bool,
|
||||
buffer: Vec<I::Item>,
|
||||
}
|
||||
|
||||
impl<I> LazyBuffer<I>
|
||||
where I: Iterator
|
||||
{
|
||||
pub fn new(it: I) -> LazyBuffer<I> {
|
||||
let mut it = it;
|
||||
let mut buffer = Vec::new();
|
||||
let done;
|
||||
if let Some(first) = it.next() {
|
||||
buffer.push(first);
|
||||
done = false;
|
||||
} else {
|
||||
done = true;
|
||||
}
|
||||
LazyBuffer {
|
||||
it: it,
|
||||
done: done,
|
||||
buffer: buffer,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.buffer.len()
|
||||
}
|
||||
|
||||
pub fn is_done(&self) -> bool {
|
||||
self.done
|
||||
}
|
||||
|
||||
pub fn get_next(&mut self) -> bool {
|
||||
if self.done {
|
||||
return false;
|
||||
}
|
||||
let next_item = self.it.next();
|
||||
match next_item {
|
||||
Some(x) => {
|
||||
self.buffer.push(x);
|
||||
true
|
||||
}
|
||||
None => {
|
||||
self.done = true;
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> Index<usize> for LazyBuffer<I>
|
||||
where I: Iterator,
|
||||
I::Item: Sized
|
||||
{
|
||||
type Output = I::Item;
|
||||
|
||||
fn index<'b>(&'b self, _index: usize) -> &'b I::Item {
|
||||
self.buffer.index(_index)
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator to iterate through all the `n`-length combinations in an iterator.
|
||||
///
|
||||
/// See [`.combinations()`](../trait.Itertools.html#method.combinations) for more information.
|
||||
pub struct Combinations<I: Iterator> {
|
||||
n: usize,
|
||||
indices: Vec<usize>,
|
||||
pool: LazyBuffer<I>,
|
||||
first: bool,
|
||||
}
|
||||
|
||||
impl<I> fmt::Debug for Combinations<I>
|
||||
where I: Iterator + fmt::Debug,
|
||||
I::Item: fmt::Debug,
|
||||
{
|
||||
debug_fmt_fields!(Combinations, n, indices, pool, first);
|
||||
}
|
||||
|
||||
/// Create a new `Combinations` from a clonable iterator.
|
||||
///
|
||||
/// **Panics** if `n` is zero.
|
||||
pub fn combinations<I>(iter: I, n: usize) -> Combinations<I>
|
||||
where I: Iterator
|
||||
{
|
||||
assert!(n != 0);
|
||||
let mut indices: Vec<usize> = Vec::with_capacity(n);
|
||||
for i in 0..n {
|
||||
indices.push(i);
|
||||
}
|
||||
let mut pool: LazyBuffer<I> = LazyBuffer::new(iter);
|
||||
|
||||
for _ in 0..n {
|
||||
if !pool.get_next() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Combinations {
|
||||
n: n,
|
||||
indices: indices,
|
||||
pool: pool,
|
||||
first: true,
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> Iterator for Combinations<I>
|
||||
where I: Iterator,
|
||||
I::Item: Clone
|
||||
{
|
||||
type Item = Vec<I::Item>;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut pool_len = self.pool.len();
|
||||
if self.pool.is_done() {
|
||||
if pool_len == 0 || self.n > pool_len {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
if self.first {
|
||||
self.first = false;
|
||||
} else {
|
||||
// Scan from the end, looking for an index to increment
|
||||
let mut i: usize = self.n - 1;
|
||||
|
||||
// Check if we need to consume more from the iterator
|
||||
if self.indices[i] == pool_len - 1 && !self.pool.is_done() {
|
||||
if self.pool.get_next() {
|
||||
pool_len += 1;
|
||||
}
|
||||
}
|
||||
|
||||
while self.indices[i] == i + pool_len - self.n {
|
||||
if i > 0 {
|
||||
i -= 1;
|
||||
} else {
|
||||
// Reached the last combination
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
// Increment index, and reset the ones to its right
|
||||
self.indices[i] += 1;
|
||||
let mut j = i + 1;
|
||||
while j < self.n {
|
||||
self.indices[j] = self.indices[j - 1] + 1;
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Create result vector based on the indices
|
||||
let mut result = Vec::with_capacity(self.n);
|
||||
for i in self.indices.iter() {
|
||||
result.push(self.pool[*i].clone());
|
||||
}
|
||||
Some(result)
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator adapter to filter out duplicate elements.
|
||||
///
|
||||
/// See [`.unique_by()`](../trait.Itertools.html#method.unique) for more information.
|
||||
#[derive(Clone)]
|
||||
pub struct UniqueBy<I: Iterator, V, F> {
|
||||
iter: I,
|
||||
used: HashSet<V>,
|
||||
f: F,
|
||||
}
|
||||
|
||||
impl<I, V, F> fmt::Debug for UniqueBy<I, V, F>
|
||||
where I: Iterator + fmt::Debug,
|
||||
V: fmt::Debug + Hash + Eq,
|
||||
{
|
||||
debug_fmt_fields!(UniqueBy, iter, used);
|
||||
}
|
||||
|
||||
/// Create a new `UniqueBy` iterator.
|
||||
pub fn unique_by<I, V, F>(iter: I, f: F) -> UniqueBy<I, V, F>
|
||||
where V: Eq + Hash,
|
||||
F: FnMut(&I::Item) -> V,
|
||||
I: Iterator,
|
||||
{
|
||||
UniqueBy {
|
||||
iter: iter,
|
||||
used: HashSet::new(),
|
||||
f: f,
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, V, F> Iterator for UniqueBy<I, V, F>
|
||||
where I: Iterator,
|
||||
V: Eq + Hash,
|
||||
F: FnMut(&I::Item) -> V
|
||||
{
|
||||
type Item = I::Item;
|
||||
|
||||
fn next(&mut self) -> Option<I::Item> {
|
||||
loop {
|
||||
match self.iter.next() {
|
||||
None => return None,
|
||||
Some(v) => {
|
||||
let key = (self.f)(&v);
|
||||
if self.used.insert(key) {
|
||||
return Some(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let (low, hi) = self.iter.size_hint();
|
||||
((low > 0 && self.used.is_empty()) as usize, hi)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> Iterator for Unique<I>
|
||||
where I: Iterator,
|
||||
I::Item: Eq + Hash + Clone
|
||||
{
|
||||
type Item = I::Item;
|
||||
|
||||
fn next(&mut self) -> Option<I::Item> {
|
||||
loop {
|
||||
match self.iter.iter.next() {
|
||||
None => return None,
|
||||
Some(v) => {
|
||||
if !self.iter.used.contains(&v) {
|
||||
// FIXME: Avoid this double lookup when the entry api allows
|
||||
self.iter.used.insert(v.clone());
|
||||
return Some(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let (low, hi) = self.iter.iter.size_hint();
|
||||
((low > 0 && self.iter.used.is_empty()) as usize, hi)
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator adapter to filter out duplicate elements.
|
||||
///
|
||||
/// See [`.unique()`](../trait.Itertools.html#method.unique) for more information.
|
||||
#[derive(Clone)]
|
||||
pub struct Unique<I: Iterator> {
|
||||
iter: UniqueBy<I, I::Item, ()>,
|
||||
}
|
||||
|
||||
impl<I> fmt::Debug for Unique<I>
|
||||
where I: Iterator + fmt::Debug,
|
||||
I::Item: Hash + Eq + fmt::Debug,
|
||||
{
|
||||
debug_fmt_fields!(Unique, iter);
|
||||
}
|
||||
|
||||
pub fn unique<I>(iter: I) -> Unique<I>
|
||||
where I: Iterator,
|
||||
I::Item: Eq + Hash,
|
||||
{
|
||||
Unique {
|
||||
iter: UniqueBy {
|
||||
iter: iter,
|
||||
used: HashSet::new(),
|
||||
f: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator adapter to simply flatten a structure.
|
||||
///
|
||||
/// See [`.flatten()`](../trait.Itertools.html#method.flatten) for more information.
|
||||
#[derive(Clone, Debug)]
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct Flatten<I, J> {
|
||||
iter: I,
|
||||
front: Option<J>,
|
||||
back: Option<J>,
|
||||
}
|
||||
|
||||
/// Create a new `Flatten` iterator.
|
||||
|
@ -1358,7 +1050,6 @@ pub fn flatten<I, J>(iter: I) -> Flatten<I, J> {
|
|||
Flatten {
|
||||
iter: iter,
|
||||
front: None,
|
||||
back: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1382,12 +1073,6 @@ impl<I, J> Iterator for Flatten<I, J>
|
|||
break;
|
||||
}
|
||||
}
|
||||
if let Some(ref mut b) = self.back {
|
||||
match b.next() {
|
||||
elt @ Some(_) => return elt,
|
||||
None => { }
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
|
@ -1399,48 +1084,15 @@ impl<I, J> Iterator for Flatten<I, J>
|
|||
if let Some(iter) = self.front {
|
||||
accum = fold(iter, accum, &mut f);
|
||||
}
|
||||
for iter in self.iter {
|
||||
accum = fold(iter, accum, &mut f);
|
||||
self.iter.fold(accum, move |accum, iter| fold(iter, accum, &mut f))
|
||||
}
|
||||
if let Some(iter) = self.back {
|
||||
accum = fold(iter, accum, &mut f);
|
||||
}
|
||||
accum
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, J> DoubleEndedIterator for Flatten<I, J>
|
||||
where I: DoubleEndedIterator,
|
||||
I::Item: IntoIterator<IntoIter=J, Item=J::Item>,
|
||||
J: DoubleEndedIterator,
|
||||
{
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
if let Some(ref mut b) = self.back {
|
||||
match b.next_back() {
|
||||
elt @ Some(_) => return elt,
|
||||
None => { }
|
||||
}
|
||||
}
|
||||
if let Some(next_back) = self.iter.next_back() {
|
||||
self.back = Some(next_back.into_iter());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if let Some(ref mut f) = self.front {
|
||||
match f.next_back() {
|
||||
elt @ Some(_) => return elt,
|
||||
None => { }
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator adapter to apply a transformation within a nested `Result`.
|
||||
///
|
||||
/// See [`.map_results()`](../trait.Itertools.html#method.map_results) for more information.
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct MapResults<I, F> {
|
||||
iter: I,
|
||||
f: F
|
||||
|
@ -1470,4 +1122,151 @@ impl<I, F, T, U, E> Iterator for MapResults<I, F>
|
|||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iter.size_hint()
|
||||
}
|
||||
|
||||
fn fold<Acc, Fold>(self, init: Acc, mut fold_f: Fold) -> Acc
|
||||
where Fold: FnMut(Acc, Self::Item) -> Acc,
|
||||
{
|
||||
let mut f = self.f;
|
||||
self.iter.fold(init, move |acc, v| fold_f(acc, v.map(&mut f)))
|
||||
}
|
||||
|
||||
fn collect<C>(self) -> C
|
||||
where C: FromIterator<Self::Item>
|
||||
{
|
||||
let mut f = self.f;
|
||||
self.iter.map(move |v| v.map(&mut f)).collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator adapter to get the positions of each element that matches a predicate.
|
||||
///
|
||||
/// See [`.positions()`](../trait.Itertools.html#method.positions) for more information.
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct Positions<I, F> {
|
||||
iter: I,
|
||||
f: F,
|
||||
count: usize,
|
||||
}
|
||||
|
||||
/// Create a new `Positions` iterator.
|
||||
pub fn positions<I, F>(iter: I, f: F) -> Positions<I, F>
|
||||
where I: Iterator,
|
||||
F: FnMut(I::Item) -> bool,
|
||||
{
|
||||
Positions {
|
||||
iter: iter,
|
||||
f: f,
|
||||
count: 0
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, F> Iterator for Positions<I, F>
|
||||
where I: Iterator,
|
||||
F: FnMut(I::Item) -> bool,
|
||||
{
|
||||
type Item = usize;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
while let Some(v) = self.iter.next() {
|
||||
let i = self.count;
|
||||
self.count = i + 1;
|
||||
if (self.f)(v) {
|
||||
return Some(i);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(0, self.iter.size_hint().1)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, F> DoubleEndedIterator for Positions<I, F>
|
||||
where I: DoubleEndedIterator + ExactSizeIterator,
|
||||
F: FnMut(I::Item) -> bool,
|
||||
{
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
while let Some(v) = self.iter.next_back() {
|
||||
if (self.f)(v) {
|
||||
return Some(self.count + self.iter.len())
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator adapter to apply a mutating function to each element before yielding it.
|
||||
///
|
||||
/// See [`.update()`](../trait.Itertools.html#method.update) for more information.
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct Update<I, F> {
|
||||
iter: I,
|
||||
f: F,
|
||||
}
|
||||
|
||||
/// Create a new `Update` iterator.
|
||||
pub fn update<I, F>(iter: I, f: F) -> Update<I, F>
|
||||
where
|
||||
I: Iterator,
|
||||
F: FnMut(&mut I::Item),
|
||||
{
|
||||
Update { iter: iter, f: f }
|
||||
}
|
||||
|
||||
impl<I, F> Iterator for Update<I, F>
|
||||
where
|
||||
I: Iterator,
|
||||
F: FnMut(&mut I::Item),
|
||||
{
|
||||
type Item = I::Item;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let Some(mut v) = self.iter.next() {
|
||||
(self.f)(&mut v);
|
||||
Some(v)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iter.size_hint()
|
||||
}
|
||||
|
||||
fn fold<Acc, G>(self, init: Acc, mut g: G) -> Acc
|
||||
where G: FnMut(Acc, Self::Item) -> Acc,
|
||||
{
|
||||
let mut f = self.f;
|
||||
self.iter.fold(init, move |acc, mut v| { f(&mut v); g(acc, v) })
|
||||
}
|
||||
|
||||
// if possible, re-use inner iterator specializations in collect
|
||||
fn collect<C>(self) -> C
|
||||
where C: FromIterator<Self::Item>
|
||||
{
|
||||
let mut f = self.f;
|
||||
self.iter.map(move |mut v| { f(&mut v); v }).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, F> ExactSizeIterator for Update<I, F>
|
||||
where
|
||||
I: ExactSizeIterator,
|
||||
F: FnMut(&mut I::Item),
|
||||
{}
|
||||
|
||||
impl<I, F> DoubleEndedIterator for Update<I, F>
|
||||
where
|
||||
I: DoubleEndedIterator,
|
||||
F: FnMut(&mut I::Item),
|
||||
{
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
if let Some(mut v) = self.iter.next_back() {
|
||||
(self.f)(&mut v);
|
||||
Some(v)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,219 @@
|
|||
#![cfg(feature = "use_std")]
|
||||
|
||||
use size_hint;
|
||||
use Itertools;
|
||||
|
||||
#[derive(Clone)]
|
||||
/// An iterator adaptor that iterates over the cartesian product of
|
||||
/// multiple iterators of type `I`.
|
||||
///
|
||||
/// An iterator element type is `Vec<I>`.
|
||||
///
|
||||
/// See [`.multi_cartesian_product()`](../trait.Itertools.html#method.multi_cartesian_product)
|
||||
/// for more information.
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct MultiProduct<I>(Vec<MultiProductIter<I>>)
|
||||
where I: Iterator + Clone,
|
||||
I::Item: Clone;
|
||||
|
||||
/// Create a new cartesian product iterator over an arbitrary number
|
||||
/// of iterators of the same type.
|
||||
///
|
||||
/// Iterator element is of type `Vec<H::Item::Item>`.
|
||||
pub fn multi_cartesian_product<H>(iters: H) -> MultiProduct<<H::Item as IntoIterator>::IntoIter>
|
||||
where H: Iterator,
|
||||
H::Item: IntoIterator,
|
||||
<H::Item as IntoIterator>::IntoIter: Clone,
|
||||
<H::Item as IntoIterator>::Item: Clone
|
||||
{
|
||||
MultiProduct(iters.map(|i| MultiProductIter::new(i.into_iter())).collect())
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
/// Holds the state of a single iterator within a MultiProduct.
|
||||
struct MultiProductIter<I>
|
||||
where I: Iterator + Clone,
|
||||
I::Item: Clone
|
||||
{
|
||||
cur: Option<I::Item>,
|
||||
iter: I,
|
||||
iter_orig: I,
|
||||
}
|
||||
|
||||
/// Holds the current state during an iteration of a MultiProduct.
|
||||
enum MultiProductIterState {
|
||||
StartOfIter,
|
||||
MidIter { on_first_iter: bool },
|
||||
}
|
||||
|
||||
impl<I> MultiProduct<I>
|
||||
where I: Iterator + Clone,
|
||||
I::Item: Clone
|
||||
{
|
||||
/// Iterates the rightmost iterator, then recursively iterates iterators
|
||||
/// to the left if necessary.
|
||||
///
|
||||
/// Returns true if the iteration succeeded, else false.
|
||||
fn iterate_last(
|
||||
multi_iters: &mut [MultiProductIter<I>],
|
||||
mut state: MultiProductIterState
|
||||
) -> bool {
|
||||
use self::MultiProductIterState::*;
|
||||
|
||||
if let Some((last, rest)) = multi_iters.split_last_mut() {
|
||||
let on_first_iter = match state {
|
||||
StartOfIter => {
|
||||
let on_first_iter = !last.in_progress();
|
||||
state = MidIter { on_first_iter: on_first_iter };
|
||||
on_first_iter
|
||||
},
|
||||
MidIter { on_first_iter } => on_first_iter
|
||||
};
|
||||
|
||||
if !on_first_iter {
|
||||
last.iterate();
|
||||
}
|
||||
|
||||
if last.in_progress() {
|
||||
true
|
||||
} else if MultiProduct::iterate_last(rest, state) {
|
||||
last.reset();
|
||||
last.iterate();
|
||||
// If iterator is None twice consecutively, then iterator is
|
||||
// empty; whole product is empty.
|
||||
last.in_progress()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
// Reached end of iterator list. On initialisation, return true.
|
||||
// At end of iteration (final iterator finishes), finish.
|
||||
match state {
|
||||
StartOfIter => false,
|
||||
MidIter { on_first_iter } => on_first_iter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the unwrapped value of the next iteration.
|
||||
fn curr_iterator(&self) -> Vec<I::Item> {
|
||||
self.0.iter().map(|multi_iter| {
|
||||
multi_iter.cur.clone().unwrap()
|
||||
}).collect()
|
||||
}
|
||||
|
||||
/// Returns true if iteration has started and has not yet finished; false
|
||||
/// otherwise.
|
||||
fn in_progress(&self) -> bool {
|
||||
if let Some(last) = self.0.last() {
|
||||
last.in_progress()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> MultiProductIter<I>
|
||||
where I: Iterator + Clone,
|
||||
I::Item: Clone
|
||||
{
|
||||
fn new(iter: I) -> Self {
|
||||
MultiProductIter {
|
||||
cur: None,
|
||||
iter: iter.clone(),
|
||||
iter_orig: iter
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterate the managed iterator.
|
||||
fn iterate(&mut self) {
|
||||
self.cur = self.iter.next();
|
||||
}
|
||||
|
||||
/// Reset the managed iterator.
|
||||
fn reset(&mut self) {
|
||||
self.iter = self.iter_orig.clone();
|
||||
}
|
||||
|
||||
/// Returns true if the current iterator has been started and has not yet
|
||||
/// finished; false otherwise.
|
||||
fn in_progress(&self) -> bool {
|
||||
self.cur.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> Iterator for MultiProduct<I>
|
||||
where I: Iterator + Clone,
|
||||
I::Item: Clone
|
||||
{
|
||||
type Item = Vec<I::Item>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if MultiProduct::iterate_last(
|
||||
&mut self.0,
|
||||
MultiProductIterState::StartOfIter
|
||||
) {
|
||||
Some(self.curr_iterator())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn count(self) -> usize {
|
||||
if self.0.len() == 0 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if !self.in_progress() {
|
||||
return self.0.into_iter().fold(1, |acc, multi_iter| {
|
||||
acc * multi_iter.iter.count()
|
||||
});
|
||||
}
|
||||
|
||||
self.0.into_iter().fold(
|
||||
0,
|
||||
|acc, MultiProductIter { iter, iter_orig, cur: _ }| {
|
||||
let total_count = iter_orig.count();
|
||||
let cur_count = iter.count();
|
||||
acc * total_count + cur_count
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
// Not ExactSizeIterator because size may be larger than usize
|
||||
if self.0.len() == 0 {
|
||||
return (0, Some(0));
|
||||
}
|
||||
|
||||
if !self.in_progress() {
|
||||
return self.0.iter().fold((1, Some(1)), |acc, multi_iter| {
|
||||
size_hint::mul(acc, multi_iter.iter.size_hint())
|
||||
});
|
||||
}
|
||||
|
||||
self.0.iter().fold(
|
||||
(0, Some(0)),
|
||||
|acc, &MultiProductIter { ref iter, ref iter_orig, cur: _ }| {
|
||||
let cur_size = iter.size_hint();
|
||||
let total_size = iter_orig.size_hint();
|
||||
size_hint::add(size_hint::mul(acc, total_size), cur_size)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn last(self) -> Option<Self::Item> {
|
||||
let iter_count = self.0.len();
|
||||
|
||||
let lasts: Self::Item = self.0.into_iter()
|
||||
.map(|multi_iter| multi_iter.iter.last())
|
||||
.while_some()
|
||||
.collect();
|
||||
|
||||
if lasts.len() == iter_count {
|
||||
Some(lasts)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
|
||||
use std::ops::Index;
|
||||
use std::fmt;
|
||||
|
||||
/// An iterator to iterate through all the `n`-length combinations in an iterator.
|
||||
///
|
||||
/// See [`.combinations()`](../trait.Itertools.html#method.combinations) for more information.
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct Combinations<I: Iterator> {
|
||||
n: usize,
|
||||
indices: Vec<usize>,
|
||||
pool: LazyBuffer<I>,
|
||||
first: bool,
|
||||
}
|
||||
|
||||
impl<I> fmt::Debug for Combinations<I>
|
||||
where I: Iterator + fmt::Debug,
|
||||
I::Item: fmt::Debug,
|
||||
{
|
||||
debug_fmt_fields!(Combinations, n, indices, pool, first);
|
||||
}
|
||||
|
||||
/// Create a new `Combinations` from a clonable iterator.
|
||||
pub fn combinations<I>(iter: I, n: usize) -> Combinations<I>
|
||||
where I: Iterator
|
||||
{
|
||||
let mut indices: Vec<usize> = Vec::with_capacity(n);
|
||||
for i in 0..n {
|
||||
indices.push(i);
|
||||
}
|
||||
let mut pool: LazyBuffer<I> = LazyBuffer::new(iter);
|
||||
|
||||
for _ in 0..n {
|
||||
if !pool.get_next() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Combinations {
|
||||
n: n,
|
||||
indices: indices,
|
||||
pool: pool,
|
||||
first: true,
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> Iterator for Combinations<I>
|
||||
where I: Iterator,
|
||||
I::Item: Clone
|
||||
{
|
||||
type Item = Vec<I::Item>;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut pool_len = self.pool.len();
|
||||
if self.pool.is_done() {
|
||||
if pool_len == 0 || self.n > pool_len {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
if self.first {
|
||||
self.first = false;
|
||||
} else if self.n == 0 {
|
||||
return None;
|
||||
} else {
|
||||
// Scan from the end, looking for an index to increment
|
||||
let mut i: usize = self.n - 1;
|
||||
|
||||
// Check if we need to consume more from the iterator
|
||||
if self.indices[i] == pool_len - 1 && !self.pool.is_done() {
|
||||
if self.pool.get_next() {
|
||||
pool_len += 1;
|
||||
}
|
||||
}
|
||||
|
||||
while self.indices[i] == i + pool_len - self.n {
|
||||
if i > 0 {
|
||||
i -= 1;
|
||||
} else {
|
||||
// Reached the last combination
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
// Increment index, and reset the ones to its right
|
||||
self.indices[i] += 1;
|
||||
let mut j = i + 1;
|
||||
while j < self.n {
|
||||
self.indices[j] = self.indices[j - 1] + 1;
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Create result vector based on the indices
|
||||
let mut result = Vec::with_capacity(self.n);
|
||||
for i in self.indices.iter() {
|
||||
result.push(self.pool[*i].clone());
|
||||
}
|
||||
Some(result)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct LazyBuffer<I: Iterator> {
|
||||
it: I,
|
||||
done: bool,
|
||||
buffer: Vec<I::Item>,
|
||||
}
|
||||
|
||||
impl<I> LazyBuffer<I>
|
||||
where I: Iterator
|
||||
{
|
||||
pub fn new(it: I) -> LazyBuffer<I> {
|
||||
let mut it = it;
|
||||
let mut buffer = Vec::new();
|
||||
let done;
|
||||
if let Some(first) = it.next() {
|
||||
buffer.push(first);
|
||||
done = false;
|
||||
} else {
|
||||
done = true;
|
||||
}
|
||||
LazyBuffer {
|
||||
it: it,
|
||||
done: done,
|
||||
buffer: buffer,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.buffer.len()
|
||||
}
|
||||
|
||||
pub fn is_done(&self) -> bool {
|
||||
self.done
|
||||
}
|
||||
|
||||
pub fn get_next(&mut self) -> bool {
|
||||
if self.done {
|
||||
return false;
|
||||
}
|
||||
let next_item = self.it.next();
|
||||
match next_item {
|
||||
Some(x) => {
|
||||
self.buffer.push(x);
|
||||
true
|
||||
}
|
||||
None => {
|
||||
self.done = true;
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> Index<usize> for LazyBuffer<I>
|
||||
where I: Iterator,
|
||||
I::Item: Sized
|
||||
{
|
||||
type Output = I::Item;
|
||||
|
||||
fn index<'b>(&'b self, _index: usize) -> &'b I::Item {
|
||||
self.buffer.index(_index)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
use Itertools;
|
||||
|
||||
/// Combine all an iterator's elements into one element by using `Extend`.
|
||||
///
|
||||
/// `IntoIterator`-enabled version of `.concat()`
|
||||
///
|
||||
/// This combinator will extend the first item with each of the rest of the
|
||||
/// items of the iterator. If the iterator is empty, the default value of
|
||||
/// `I::Item` is returned.
|
||||
///
|
||||
/// ```rust
|
||||
/// use itertools::concat;
|
||||
///
|
||||
/// let input = vec![vec![1], vec![2, 3], vec![4, 5, 6]];
|
||||
/// assert_eq!(concat(input), vec![1, 2, 3, 4, 5, 6]);
|
||||
/// ```
|
||||
pub fn concat<I>(iterable: I) -> I::Item
|
||||
where I: IntoIterator,
|
||||
I::Item: Extend<<<I as IntoIterator>::Item as IntoIterator>::Item> + IntoIterator + Default
|
||||
{
|
||||
iterable.into_iter().fold1(|mut a, b| { a.extend(b); a }).unwrap_or_else(|| <_>::default())
|
||||
}
|
|
@ -16,6 +16,11 @@ macro_rules! impl_cons_iter(
|
|||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iter.size_hint()
|
||||
}
|
||||
fn fold<Acc, Fold>(self, accum: Acc, mut f: Fold) -> Acc
|
||||
where Fold: FnMut(Acc, Self::Item) -> Acc,
|
||||
{
|
||||
self.iter.fold(accum, move |acc, (($($B,)*), x)| f(acc, ($($B,)* x, )))
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
|
@ -36,6 +41,7 @@ impl_cons_iter!(A, B, C, D, E, F, G, H,);
|
|||
/// `((A, B), C)` to an iterator of `(A, B, C)`.
|
||||
///
|
||||
/// Used by the `iproduct!()` macro.
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct ConsTuples<I, J>
|
||||
where I: Iterator<Item=J>,
|
||||
{
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
/// Value that either holds a single A or B, or both.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum EitherOrBoth<A, B> {
|
||||
/// Both values are present.
|
||||
Both(A, B),
|
||||
/// Only the left value of type `A` is present.
|
||||
Left(A),
|
||||
/// Only the right value of type `B` is present.
|
||||
Right(B),
|
||||
}
|
|
@ -3,19 +3,26 @@
|
|||
//! The benefit of free functions is that they accept any `IntoIterator` as
|
||||
//! argument, so the resulting code may be easier to read.
|
||||
|
||||
#[cfg(feature = "use_std")]
|
||||
use std::fmt::Display;
|
||||
use std::iter::{self, Zip};
|
||||
#[cfg(feature = "use_std")]
|
||||
use Itertools;
|
||||
|
||||
pub use adaptors::{
|
||||
interleave,
|
||||
merge,
|
||||
put_back,
|
||||
put_back_n,
|
||||
};
|
||||
pub use adaptors::multipeek::multipeek;
|
||||
#[cfg(feature = "use_std")]
|
||||
pub use put_back_n_impl::put_back_n;
|
||||
#[cfg(feature = "use_std")]
|
||||
pub use multipeek_impl::multipeek;
|
||||
#[cfg(feature = "use_std")]
|
||||
pub use kmerge_impl::kmerge;
|
||||
pub use zip_eq_impl::zip_eq;
|
||||
pub use merge_join::merge_join_by;
|
||||
#[cfg(feature = "use_std")]
|
||||
pub use rciter_impl::rciter;
|
||||
|
||||
/// Iterate `iterable` with a running index.
|
||||
|
@ -196,6 +203,7 @@ pub fn min<I>(iterable: I) -> Option<I::Item>
|
|||
///
|
||||
/// assert_eq!(join(&[1, 2, 3], ", "), "1, 2, 3");
|
||||
/// ```
|
||||
#[cfg(feature = "use_std")]
|
||||
pub fn join<I>(iterable: I, sep: &str) -> String
|
||||
where I: IntoIterator,
|
||||
I::Item: Display
|
||||
|
@ -213,6 +221,7 @@ pub fn join<I>(iterable: I, sep: &str) -> String
|
|||
///
|
||||
/// assert_equal(sorted("rust".chars()), "rstu".chars());
|
||||
/// ```
|
||||
#[cfg(feature = "use_std")]
|
||||
pub fn sorted<I>(iterable: I) -> Vec<I::Item>
|
||||
where I: IntoIterator,
|
||||
I::Item: Ord
|
||||
|
|
|
@ -60,13 +60,14 @@ struct GroupInner<K, I, F>
|
|||
/// flag set if iterator is exhausted
|
||||
done: bool,
|
||||
/// Index of group we are currently buffering or visiting
|
||||
top: usize,
|
||||
top_group: usize,
|
||||
/// Least index for which we still have elements buffered
|
||||
bot: usize,
|
||||
/// Group index for `buffer[0]` -- the slots bufbot..bot are unused
|
||||
/// and will be erased when that range is large enough.
|
||||
bufbot: usize,
|
||||
/// Buffered groups, from `bufbot` (index 0) to `top`.
|
||||
oldest_buffered_group: usize,
|
||||
/// Group index for `buffer[0]` -- the slots
|
||||
/// bottom_group..oldest_buffered_group are unused and will be erased when
|
||||
/// that range is large enough.
|
||||
bottom_group: usize,
|
||||
/// Buffered groups, from `bottom_group` (index 0) to `top_group`.
|
||||
buffer: Vec<vec::IntoIter<I::Item>>,
|
||||
/// index of last group iter that was dropped, usize::MAX == none
|
||||
dropped_group: usize,
|
||||
|
@ -81,19 +82,21 @@ impl<K, I, F> GroupInner<K, I, F>
|
|||
#[inline(always)]
|
||||
fn step(&mut self, client: usize) -> Option<I::Item> {
|
||||
/*
|
||||
println!("client={}, bufbot={}, bot={}, top={}, buffers=[{}]",
|
||||
client, self.bufbot, self.bot, self.top,
|
||||
println!("client={}, bottom_group={}, oldest_buffered_group={}, top_group={}, buffers=[{}]",
|
||||
client, self.bottom_group, self.oldest_buffered_group,
|
||||
self.top_group,
|
||||
self.buffer.iter().map(|elt| elt.len()).format(", "));
|
||||
*/
|
||||
if client < self.bot {
|
||||
if client < self.oldest_buffered_group {
|
||||
None
|
||||
} else if client < self.top ||
|
||||
(client == self.top && self.buffer.len() > self.top - self.bufbot)
|
||||
} else if client < self.top_group ||
|
||||
(client == self.top_group &&
|
||||
self.buffer.len() > self.top_group - self.bottom_group)
|
||||
{
|
||||
self.lookup_buffer(client)
|
||||
} else if self.done {
|
||||
None
|
||||
} else if self.top == client {
|
||||
} else if self.top_group == client {
|
||||
self.step_current()
|
||||
} else {
|
||||
self.step_buffering(client)
|
||||
|
@ -103,24 +106,24 @@ impl<K, I, F> GroupInner<K, I, F>
|
|||
#[inline(never)]
|
||||
fn lookup_buffer(&mut self, client: usize) -> Option<I::Item> {
|
||||
// if `bufidx` doesn't exist in self.buffer, it might be empty
|
||||
let bufidx = client - self.bufbot;
|
||||
if client < self.bot {
|
||||
let bufidx = client - self.bottom_group;
|
||||
if client < self.oldest_buffered_group {
|
||||
return None;
|
||||
}
|
||||
let elt = self.buffer.get_mut(bufidx).and_then(|queue| queue.next());
|
||||
if elt.is_none() && client == self.bot {
|
||||
if elt.is_none() && client == self.oldest_buffered_group {
|
||||
// FIXME: VecDeque is unfortunately not zero allocation when empty,
|
||||
// so we do this job manually.
|
||||
// `bufbot..bot` is unused, and if it's large enough, erase it.
|
||||
self.bot += 1;
|
||||
// `bottom_group..oldest_buffered_group` is unused, and if it's large enough, erase it.
|
||||
self.oldest_buffered_group += 1;
|
||||
// skip forward further empty queues too
|
||||
while self.buffer.get(self.bot - self.bufbot)
|
||||
while self.buffer.get(self.oldest_buffered_group - self.bottom_group)
|
||||
.map_or(false, |buf| buf.len() == 0)
|
||||
{
|
||||
self.bot += 1;
|
||||
self.oldest_buffered_group += 1;
|
||||
}
|
||||
|
||||
let nclear = self.bot - self.bufbot;
|
||||
let nclear = self.oldest_buffered_group - self.bottom_group;
|
||||
if nclear > 0 && nclear >= self.buffer.len() / 2 {
|
||||
let mut i = 0;
|
||||
self.buffer.retain(|buf| {
|
||||
|
@ -128,7 +131,7 @@ impl<K, I, F> GroupInner<K, I, F>
|
|||
debug_assert!(buf.len() == 0 || i > nclear);
|
||||
i > nclear
|
||||
});
|
||||
self.bufbot = self.bot;
|
||||
self.bottom_group = self.oldest_buffered_group;
|
||||
}
|
||||
}
|
||||
elt
|
||||
|
@ -152,12 +155,12 @@ impl<K, I, F> GroupInner<K, I, F>
|
|||
// the requested group index, and buffer the elements (unless
|
||||
// the group is marked as dropped).
|
||||
// Because the `Groups` iterator is always the first to request
|
||||
// each group index, client is the next index efter top.
|
||||
debug_assert!(self.top + 1 == client);
|
||||
// each group index, client is the next index efter top_group.
|
||||
debug_assert!(self.top_group + 1 == client);
|
||||
let mut group = Vec::new();
|
||||
|
||||
if let Some(elt) = self.current_elt.take() {
|
||||
if self.top != self.dropped_group {
|
||||
if self.top_group != self.dropped_group {
|
||||
group.push(elt);
|
||||
}
|
||||
}
|
||||
|
@ -174,33 +177,33 @@ impl<K, I, F> GroupInner<K, I, F>
|
|||
},
|
||||
}
|
||||
self.current_key = Some(key);
|
||||
if self.top != self.dropped_group {
|
||||
if self.top_group != self.dropped_group {
|
||||
group.push(elt);
|
||||
}
|
||||
}
|
||||
|
||||
if self.top != self.dropped_group {
|
||||
if self.top_group != self.dropped_group {
|
||||
self.push_next_group(group);
|
||||
}
|
||||
if first_elt.is_some() {
|
||||
self.top += 1;
|
||||
debug_assert!(self.top == client);
|
||||
self.top_group += 1;
|
||||
debug_assert!(self.top_group == client);
|
||||
}
|
||||
first_elt
|
||||
}
|
||||
|
||||
fn push_next_group(&mut self, group: Vec<I::Item>) {
|
||||
// When we add a new buffered group, fill up slots between bot and top
|
||||
while self.top - self.bufbot > self.buffer.len() {
|
||||
// When we add a new buffered group, fill up slots between oldest_buffered_group and top_group
|
||||
while self.top_group - self.bottom_group > self.buffer.len() {
|
||||
if self.buffer.is_empty() {
|
||||
self.bufbot += 1;
|
||||
self.bot += 1;
|
||||
self.bottom_group += 1;
|
||||
self.oldest_buffered_group += 1;
|
||||
} else {
|
||||
self.buffer.push(Vec::new().into_iter());
|
||||
}
|
||||
}
|
||||
self.buffer.push(group.into_iter());
|
||||
debug_assert!(self.top + 1 - self.bufbot == self.buffer.len());
|
||||
debug_assert!(self.top_group + 1 - self.bottom_group == self.buffer.len());
|
||||
}
|
||||
|
||||
/// This is the immediate case, where we use no buffering
|
||||
|
@ -219,7 +222,7 @@ impl<K, I, F> GroupInner<K, I, F>
|
|||
Some(old_key) => if old_key != key {
|
||||
self.current_key = Some(key);
|
||||
self.current_elt = Some(elt);
|
||||
self.top += 1;
|
||||
self.top_group += 1;
|
||||
return None;
|
||||
},
|
||||
}
|
||||
|
@ -240,14 +243,14 @@ impl<K, I, F> GroupInner<K, I, F>
|
|||
// Perform this by simply buffering one more element, grabbing the
|
||||
// next key.
|
||||
debug_assert!(!self.done);
|
||||
debug_assert!(client == self.top);
|
||||
debug_assert!(client == self.top_group);
|
||||
debug_assert!(self.current_key.is_some());
|
||||
debug_assert!(self.current_elt.is_none());
|
||||
let old_key = self.current_key.take().unwrap();
|
||||
if let Some(elt) = self.next_element() {
|
||||
let key = self.key.call_mut(&elt);
|
||||
if old_key != key {
|
||||
self.top += 1;
|
||||
self.top_group += 1;
|
||||
}
|
||||
self.current_key = Some(key);
|
||||
self.current_elt = Some(elt);
|
||||
|
@ -281,6 +284,7 @@ impl<K, I, F> GroupInner<K, I, F>
|
|||
/// iterated.
|
||||
///
|
||||
/// See [`.group_by()`](../trait.Itertools.html#method.group_by) for more information.
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct GroupBy<K, I, F>
|
||||
where I: Iterator,
|
||||
{
|
||||
|
@ -302,9 +306,9 @@ pub fn new<K, J, F>(iter: J, f: F) -> GroupBy<K, J::IntoIter, F>
|
|||
current_key: None,
|
||||
current_elt: None,
|
||||
done: false,
|
||||
top: 0,
|
||||
bot: 0,
|
||||
bufbot: 0,
|
||||
top_group: 0,
|
||||
oldest_buffered_group: 0,
|
||||
bottom_group: 0,
|
||||
buffer: Vec::new(),
|
||||
dropped_group: !0,
|
||||
}),
|
||||
|
@ -350,6 +354,7 @@ impl<'a, K, I, F> IntoIterator for &'a GroupBy<K, I, F>
|
|||
/// the group's key `K` and the group's iterator.
|
||||
///
|
||||
/// See [`.group_by()`](../trait.Itertools.html#method.group_by) for more information.
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct Groups<'a, K: 'a, I: 'a, F: 'a>
|
||||
where I: Iterator,
|
||||
I::Item: 'a
|
||||
|
@ -431,9 +436,9 @@ pub fn new_chunks<J>(iter: J, size: usize) -> IntoChunks<J::IntoIter>
|
|||
current_key: None,
|
||||
current_elt: None,
|
||||
done: false,
|
||||
top: 0,
|
||||
bot: 0,
|
||||
bufbot: 0,
|
||||
top_group: 0,
|
||||
oldest_buffered_group: 0,
|
||||
bottom_group: 0,
|
||||
buffer: Vec::new(),
|
||||
dropped_group: !0,
|
||||
}),
|
||||
|
@ -455,6 +460,7 @@ pub fn new_chunks<J>(iter: J, size: usize) -> IntoChunks<J::IntoIter>
|
|||
/// Iterator element type is `Chunk`, each chunk's iterator.
|
||||
///
|
||||
/// See [`.chunks()`](../trait.Itertools.html#method.chunks) for more information.
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct IntoChunks<I>
|
||||
where I: Iterator,
|
||||
{
|
||||
|
@ -499,6 +505,7 @@ impl<'a, I> IntoIterator for &'a IntoChunks<I>
|
|||
/// Iterator element type is `Chunk`.
|
||||
///
|
||||
/// See [`.chunks()`](../trait.Itertools.html#method.chunks) for more information.
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct Chunks<'a, I: 'a>
|
||||
where I: Iterator,
|
||||
I::Item: 'a,
|
||||
|
|
|
@ -10,6 +10,7 @@ use super::size_hint;
|
|||
/// This iterator is *fused*.
|
||||
///
|
||||
/// See [`.intersperse()`](../trait.Itertools.html#method.intersperse) for more information.
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct Intersperse<I>
|
||||
where I: Iterator
|
||||
{
|
||||
|
|
|
@ -109,6 +109,7 @@ fn sift_down<T, S>(heap: &mut [T], index: usize, mut less_than: S)
|
|||
/// Iterator element type is `I::Item`.
|
||||
///
|
||||
/// See [`.kmerge()`](../trait.Itertools.html#method.kmerge) for more information.
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct KMerge<I>
|
||||
where I: Iterator
|
||||
{
|
||||
|
@ -183,6 +184,7 @@ impl<I> Iterator for KMerge<I>
|
|||
///
|
||||
/// See [`.kmerge_by()`](../trait.Itertools.html#method.kmerge_by) for more
|
||||
/// information.
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct KMergeBy<I, F>
|
||||
where I: Iterator,
|
||||
{
|
||||
|
|
|
@ -1,45 +1,51 @@
|
|||
#![warn(missing_docs)]
|
||||
#![crate_name="itertools"]
|
||||
#![cfg_attr(not(feature = "use_std"), no_std)]
|
||||
|
||||
//! Itertools — extra iterator adaptors, functions and macros.
|
||||
//!
|
||||
//! To use the iterator methods in this crate, import the [`Itertools` trait](./trait.Itertools.html):
|
||||
//!
|
||||
//! ```ignore
|
||||
//! ```
|
||||
//! use itertools::Itertools;
|
||||
//! ```
|
||||
//!
|
||||
//! To enable the macros in this crate, use the `#[macro_use]` attribute:
|
||||
//! ## Crate Features
|
||||
//!
|
||||
//! ```ignore
|
||||
//! #[macro_use] extern crate itertools;
|
||||
//! ```
|
||||
//! - `use_std`
|
||||
//! - Enabled by default.
|
||||
//! - Disable to compile itertools using `#![no_std]`. This disables
|
||||
//! any items that depend on collections (like `group_by`, `unique`,
|
||||
//! `kmerge`, `join` and many more).
|
||||
//!
|
||||
//! ## License
|
||||
//! Dual-licensed to be compatible with the Rust project.
|
||||
//! ## Rust Version
|
||||
//!
|
||||
//! Licensed under the Apache License, Version 2.0
|
||||
//! http://www.apache.org/licenses/LICENSE-2.0 or the MIT license
|
||||
//! http://opensource.org/licenses/MIT, at your
|
||||
//! option. This file may not be copied, modified, or distributed
|
||||
//! except according to those terms.
|
||||
//! This version of itertools requires Rust 1.12 or later.
|
||||
//!
|
||||
//!
|
||||
#![doc(html_root_url="https://docs.rs/itertools/")]
|
||||
#![doc(html_root_url="https://docs.rs/itertools/0.7/")]
|
||||
|
||||
extern crate either;
|
||||
|
||||
#[cfg(not(feature = "use_std"))]
|
||||
extern crate core as std;
|
||||
|
||||
pub use either::Either;
|
||||
|
||||
use std::iter::{IntoIterator};
|
||||
use std::fmt::Write;
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt;
|
||||
#[cfg(feature = "use_std")]
|
||||
use std::hash::Hash;
|
||||
#[cfg(feature = "use_std")]
|
||||
use std::fmt::Write;
|
||||
|
||||
#[macro_use]
|
||||
mod impl_macros;
|
||||
|
||||
// for compatibility with no std and macros
|
||||
#[doc(hidden)]
|
||||
pub use std::iter as __std_iter;
|
||||
|
||||
/// The concrete iterator types.
|
||||
pub mod structs {
|
||||
pub use adaptors::{
|
||||
|
@ -48,71 +54,103 @@ pub mod structs {
|
|||
InterleaveShortest,
|
||||
Product,
|
||||
PutBack,
|
||||
PutBackN,
|
||||
Batching,
|
||||
Step,
|
||||
MapResults,
|
||||
Merge,
|
||||
MergeBy,
|
||||
MultiPeek,
|
||||
TakeWhileRef,
|
||||
WhileSome,
|
||||
Coalesce,
|
||||
TupleCombinations,
|
||||
Combinations,
|
||||
Unique,
|
||||
UniqueBy,
|
||||
Flatten,
|
||||
Positions,
|
||||
Update,
|
||||
};
|
||||
#[cfg(feature = "use_std")]
|
||||
pub use adaptors::MultiProduct;
|
||||
#[cfg(feature = "use_std")]
|
||||
pub use combinations::Combinations;
|
||||
pub use cons_tuples_impl::ConsTuples;
|
||||
pub use format::{Format, FormatWith};
|
||||
#[cfg(feature = "use_std")]
|
||||
pub use groupbylazy::{IntoChunks, Chunk, Chunks, GroupBy, Group, Groups};
|
||||
pub use intersperse::Intersperse;
|
||||
#[cfg(feature = "use_std")]
|
||||
pub use kmerge_impl::{KMerge, KMergeBy};
|
||||
pub use merge_join::MergeJoinBy;
|
||||
#[cfg(feature = "use_std")]
|
||||
pub use multipeek_impl::MultiPeek;
|
||||
pub use pad_tail::PadUsing;
|
||||
pub use peeking_take_while::PeekingTakeWhile;
|
||||
pub use process_results_impl::ProcessResults;
|
||||
#[cfg(feature = "use_std")]
|
||||
pub use put_back_n_impl::PutBackN;
|
||||
#[cfg(feature = "use_std")]
|
||||
pub use rciter_impl::RcIter;
|
||||
pub use repeatn::RepeatN;
|
||||
pub use sources::{RepeatCall, Unfold, Iterate};
|
||||
#[cfg(feature = "use_std")]
|
||||
pub use tee::Tee;
|
||||
pub use tuple_impl::{TupleBuffer, TupleWindows, Tuples};
|
||||
#[cfg(feature = "use_std")]
|
||||
pub use unique_impl::{Unique, UniqueBy};
|
||||
pub use with_position::WithPosition;
|
||||
pub use zip_eq_impl::ZipEq;
|
||||
pub use zip_longest::ZipLongest;
|
||||
pub use ziptuple::Zip;
|
||||
}
|
||||
pub use structs::*;
|
||||
pub use concat_impl::concat;
|
||||
pub use cons_tuples_impl::cons_tuples;
|
||||
pub use diff::diff_with;
|
||||
pub use diff::Diff;
|
||||
#[cfg(feature = "use_std")]
|
||||
pub use kmerge_impl::{kmerge_by};
|
||||
pub use minmax::MinMaxResult;
|
||||
pub use peeking_take_while::PeekingNext;
|
||||
pub use process_results_impl::process_results;
|
||||
pub use repeatn::repeat_n;
|
||||
pub use sources::{repeat_call, unfold, iterate};
|
||||
pub use with_position::Position;
|
||||
pub use zip_longest::EitherOrBoth;
|
||||
pub use ziptuple::multizip;
|
||||
mod adaptors;
|
||||
mod either_or_both;
|
||||
pub use either_or_both::EitherOrBoth;
|
||||
#[doc(hidden)]
|
||||
pub mod free;
|
||||
#[doc(inline)]
|
||||
pub use free::*;
|
||||
mod concat_impl;
|
||||
mod cons_tuples_impl;
|
||||
#[cfg(feature = "use_std")]
|
||||
mod combinations;
|
||||
mod diff;
|
||||
mod format;
|
||||
#[cfg(feature = "use_std")]
|
||||
mod groupbylazy;
|
||||
mod intersperse;
|
||||
#[cfg(feature = "use_std")]
|
||||
mod kmerge_impl;
|
||||
mod merge_join;
|
||||
mod minmax;
|
||||
#[cfg(feature = "use_std")]
|
||||
mod multipeek_impl;
|
||||
mod pad_tail;
|
||||
mod peeking_take_while;
|
||||
mod process_results_impl;
|
||||
#[cfg(feature = "use_std")]
|
||||
mod put_back_n_impl;
|
||||
#[cfg(feature = "use_std")]
|
||||
mod rciter_impl;
|
||||
mod repeatn;
|
||||
mod size_hint;
|
||||
mod sources;
|
||||
#[cfg(feature = "use_std")]
|
||||
mod tee;
|
||||
mod tuple_impl;
|
||||
#[cfg(feature = "use_std")]
|
||||
mod unique_impl;
|
||||
mod with_position;
|
||||
mod zip_eq_impl;
|
||||
mod zip_longest;
|
||||
|
@ -134,6 +172,14 @@ mod ziptuple;
|
|||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// **Note:** To enable the macros in this crate, use the `#[macro_use]`
|
||||
/// attribute when importing the crate:
|
||||
///
|
||||
/// ```
|
||||
/// #[macro_use] extern crate itertools;
|
||||
/// # fn main() { }
|
||||
/// ```
|
||||
macro_rules! iproduct {
|
||||
(@flatten $I:expr,) => (
|
||||
$I
|
||||
|
@ -142,7 +188,7 @@ macro_rules! iproduct {
|
|||
iproduct!(@flatten $crate::cons_tuples(iproduct!($I, $J)), $($K,)*)
|
||||
);
|
||||
($I:expr) => (
|
||||
(::std::iter::IntoIterator::into_iter($I))
|
||||
$crate::__std_iter::IntoIterator::into_iter($I)
|
||||
);
|
||||
($I:expr, $J:expr) => (
|
||||
$crate::Itertools::cartesian_product(iproduct!($I), iproduct!($J))
|
||||
|
@ -155,37 +201,66 @@ macro_rules! iproduct {
|
|||
#[macro_export]
|
||||
/// Create an iterator running multiple iterators in lockstep.
|
||||
///
|
||||
/// The izip! iterator yields elements until any subiterator
|
||||
/// The `izip!` iterator yields elements until any subiterator
|
||||
/// returns `None`.
|
||||
///
|
||||
/// Iterator element type is like `(A, B, ..., E)` if formed
|
||||
/// from iterators `(I, J, ..., M)` implementing `I: Iterator<A>`,
|
||||
/// `J: Iterator<B>`, ..., `M: Iterator<E>`
|
||||
/// This is a version of the standard ``.zip()`` that's supporting more than
|
||||
/// two iterators. The iterator elment type is a tuple with one element
|
||||
/// from each of the input iterators. Just like ``.zip()``, the iteration stops
|
||||
/// when the shortest of the inputs reaches its end.
|
||||
///
|
||||
/// **Note:** The result of this macro is an iterator composed of
|
||||
/// repeated `.zip()` and a `.map()`; it has an anonymous type.
|
||||
/// Prefer this macro `izip!()` over [`multizip`] for the performance benefits
|
||||
/// of using the standard library `.zip()`.
|
||||
///
|
||||
/// [`multizip`]: fn.multizip.html
|
||||
///
|
||||
/// ```
|
||||
/// #[macro_use] extern crate itertools;
|
||||
/// # fn main() {
|
||||
///
|
||||
/// // Iterate over three sequences side-by-side
|
||||
/// let mut xs = [0, 0, 0];
|
||||
/// let ys = [69, 107, 101];
|
||||
/// // iterate over three sequences side-by-side
|
||||
/// let mut results = [0, 0, 0, 0];
|
||||
/// let inputs = [3, 7, 9, 6];
|
||||
///
|
||||
/// for (i, a, b) in izip!(0..100, &mut xs, &ys) {
|
||||
/// *a = i ^ *b;
|
||||
/// for (r, index, input) in izip!(&mut results, 0..10, &inputs) {
|
||||
/// *r = index * 10 + input;
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(xs, [69, 106, 103]);
|
||||
/// assert_eq!(results, [0 + 3, 10 + 7, 29, 36]);
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// **Note:** To enable the macros in this crate, use the `#[macro_use]`
|
||||
/// attribute when importing the crate:
|
||||
///
|
||||
/// ```
|
||||
/// #[macro_use] extern crate itertools;
|
||||
/// # fn main() { }
|
||||
/// ```
|
||||
macro_rules! izip {
|
||||
($I:expr) => (
|
||||
(::std::iter::IntoIterator::into_iter($I))
|
||||
);
|
||||
($($I:expr),*) => (
|
||||
{
|
||||
$crate::multizip(($(izip!($I)),*))
|
||||
}
|
||||
);
|
||||
// @closure creates a tuple-flattening closure for .map() call. usage:
|
||||
// @closure partial_pattern => partial_tuple , rest , of , iterators
|
||||
// eg. izip!( @closure ((a, b), c) => (a, b, c) , dd , ee )
|
||||
( @closure $p:pat => $tup:expr ) => {
|
||||
|$p| $tup
|
||||
};
|
||||
|
||||
// The "b" identifier is a different identifier on each recursion level thanks to hygiene.
|
||||
( @closure $p:pat => ( $($tup:tt)* ) , $_iter:expr $( , $tail:expr )* ) => {
|
||||
izip!(@closure ($p, b) => ( $($tup)*, b ) $( , $tail )*)
|
||||
};
|
||||
|
||||
( $first:expr $( , $rest:expr )* $(,)* ) => {
|
||||
$crate::__std_iter::IntoIterator::into_iter($first)
|
||||
$(
|
||||
.zip($rest)
|
||||
)*
|
||||
.map(
|
||||
izip!(@closure a => (a) $( , $rest )*)
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
/// The trait `Itertools`: extra iterator adaptors and methods for iterators.
|
||||
|
@ -203,8 +278,7 @@ macro_rules! izip {
|
|||
pub trait Itertools : Iterator {
|
||||
// adaptors
|
||||
|
||||
/// Alternate elements from two iterators until both
|
||||
/// run out.
|
||||
/// Alternate elements from two iterators until both have run out.
|
||||
///
|
||||
/// Iterator element type is `Self::Item`.
|
||||
///
|
||||
|
@ -213,8 +287,8 @@ pub trait Itertools : Iterator {
|
|||
/// ```
|
||||
/// use itertools::Itertools;
|
||||
///
|
||||
/// let it = (0..3).interleave(vec![7, 8]);
|
||||
/// itertools::assert_equal(it, vec![0, 7, 1, 8, 2]);
|
||||
/// let it = (1..7).interleave(vec![-1, -2]);
|
||||
/// itertools::assert_equal(it, vec![1, -1, 2, -2, 3, 4, 5, 6]);
|
||||
/// ```
|
||||
fn interleave<J>(self, other: J) -> Interleave<Self, J::IntoIter>
|
||||
where J: IntoIterator<Item = Self::Item>,
|
||||
|
@ -223,15 +297,16 @@ pub trait Itertools : Iterator {
|
|||
interleave(self, other)
|
||||
}
|
||||
|
||||
/// Alternate elements from two iterators until one of them runs out.
|
||||
/// Alternate elements from two iterators until at least one of them has run
|
||||
/// out.
|
||||
///
|
||||
/// Iterator element type is `Self::Item`.
|
||||
///
|
||||
/// ```
|
||||
/// use itertools::Itertools;
|
||||
///
|
||||
/// let it = (0..5).interleave_shortest(vec![7, 8]);
|
||||
/// itertools::assert_equal(it, vec![0, 7, 1, 8, 2]);
|
||||
/// let it = (1..7).interleave_shortest(vec![-1, -2]);
|
||||
/// itertools::assert_equal(it, vec![1, -1, 2, -2, 3]);
|
||||
/// ```
|
||||
fn interleave_shortest<J>(self, other: J) -> InterleaveShortest<Self, J::IntoIter>
|
||||
where J: IntoIterator<Item = Self::Item>,
|
||||
|
@ -264,6 +339,15 @@ pub trait Itertools : Iterator {
|
|||
///
|
||||
/// This iterator is *fused*.
|
||||
///
|
||||
/// As long as neither input iterator is exhausted yet, it yields two values
|
||||
/// via `EitherOrBoth::Both`.
|
||||
///
|
||||
/// When the parameter iterator is exhausted, it only yields a value from the
|
||||
/// `self` iterator via `EitherOrBoth::Left`.
|
||||
///
|
||||
/// When the `self` iterator is exhausted, it only yields a value from the
|
||||
/// parameter iterator via `EitherOrBoth::Right`.
|
||||
///
|
||||
/// When both iterators return `None`, all further invocations of `.next()`
|
||||
/// will return `None`.
|
||||
///
|
||||
|
@ -306,8 +390,8 @@ pub trait Itertools : Iterator {
|
|||
/// ```
|
||||
/// use itertools::Itertools;
|
||||
///
|
||||
/// // An adaptor that gathers elements up in pairs
|
||||
/// let pit = (0..4).batching(|mut it| {
|
||||
/// // An adaptor that gathers elements in pairs
|
||||
/// let pit = (0..4).batching(|it| {
|
||||
/// match it.next() {
|
||||
/// None => None,
|
||||
/// Some(x) => match it.next() {
|
||||
|
@ -360,22 +444,15 @@ pub trait Itertools : Iterator {
|
|||
/// assert_eq!(4, group.sum::<i32>().abs());
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(feature = "use_std")]
|
||||
fn group_by<K, F>(self, key: F) -> GroupBy<K, Self, F>
|
||||
where Self: Sized,
|
||||
F: FnMut(&Self::Item) -> K,
|
||||
K: PartialEq,
|
||||
{
|
||||
groupbylazy::new(self, key)
|
||||
}
|
||||
|
||||
///
|
||||
#[deprecated(note = "renamed to .group_by()")]
|
||||
fn group_by_lazy<K, F>(self, key: F) -> GroupBy<K, Self, F>
|
||||
where Self: Sized,
|
||||
F: FnMut(&Self::Item) -> K,
|
||||
{
|
||||
self.group_by(key)
|
||||
}
|
||||
|
||||
/// Return an *iterable* that can chunk the iterator.
|
||||
///
|
||||
/// Yield subiterators (chunks) that each yield a fixed number elements,
|
||||
|
@ -403,6 +480,7 @@ pub trait Itertools : Iterator {
|
|||
/// assert_eq!(4, chunk.sum());
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(feature = "use_std")]
|
||||
fn chunks(self, size: usize) -> IntoChunks<Self>
|
||||
where Self: Sized,
|
||||
{
|
||||
|
@ -410,14 +488,6 @@ pub trait Itertools : Iterator {
|
|||
groupbylazy::new_chunks(self, size)
|
||||
}
|
||||
|
||||
///
|
||||
#[deprecated(note = "renamed to .chunks()")]
|
||||
fn chunks_lazy(self, size: usize) -> IntoChunks<Self>
|
||||
where Self: Sized,
|
||||
{
|
||||
self.chunks(size)
|
||||
}
|
||||
|
||||
/// Return an iterator over all contiguous windows producing tuples of
|
||||
/// a specific size (up to 4).
|
||||
///
|
||||
|
@ -512,6 +582,7 @@ pub trait Itertools : Iterator {
|
|||
/// itertools::assert_equal(t2, 0..4);
|
||||
/// itertools::assert_equal(t1, 1..4);
|
||||
/// ```
|
||||
#[cfg(feature = "use_std")]
|
||||
fn tee(self) -> (Tee<Self>, Tee<Self>)
|
||||
where Self: Sized,
|
||||
Self::Item: Clone
|
||||
|
@ -605,6 +676,47 @@ pub trait Itertools : Iterator {
|
|||
adaptors::merge_by_new(self, other.into_iter(), is_first)
|
||||
}
|
||||
|
||||
/// Create an iterator that merges items from both this and the specified
|
||||
/// iterator in ascending order.
|
||||
///
|
||||
/// It chooses whether to pair elements based on the `Ordering` returned by the
|
||||
/// specified compare function. At any point, inspecting the tip of the
|
||||
/// iterators `I` and `J` as items `i` of type `I::Item` and `j` of type
|
||||
/// `J::Item` respectively, the resulting iterator will:
|
||||
///
|
||||
/// - Emit `EitherOrBoth::Left(i)` when `i < j`,
|
||||
/// and remove `i` from its source iterator
|
||||
/// - Emit `EitherOrBoth::Right(j)` when `i > j`,
|
||||
/// and remove `j` from its source iterator
|
||||
/// - Emit `EitherOrBoth::Both(i, j)` when `i == j`,
|
||||
/// and remove both `i` and `j` from their respective source iterators
|
||||
///
|
||||
/// ```
|
||||
/// use itertools::Itertools;
|
||||
/// use itertools::EitherOrBoth::{Left, Right, Both};
|
||||
///
|
||||
/// let ki = (0..10).step(3);
|
||||
/// let ku = (0..10).step(5);
|
||||
/// let ki_ku = ki.merge_join_by(ku, |i, j| i.cmp(j)).map(|either| {
|
||||
/// match either {
|
||||
/// Left(_) => "Ki",
|
||||
/// Right(_) => "Ku",
|
||||
/// Both(_, _) => "KiKu"
|
||||
/// }
|
||||
/// });
|
||||
///
|
||||
/// itertools::assert_equal(ki_ku, vec!["KiKu", "Ki", "Ku", "Ki", "Ki"]);
|
||||
/// ```
|
||||
#[inline]
|
||||
fn merge_join_by<J, F>(self, other: J, cmp_fn: F) -> MergeJoinBy<Self, J::IntoIter, F>
|
||||
where J: IntoIterator,
|
||||
F: FnMut(&Self::Item, &J::Item) -> std::cmp::Ordering,
|
||||
Self: Sized
|
||||
{
|
||||
merge_join_by(self, other, cmp_fn)
|
||||
}
|
||||
|
||||
|
||||
/// Return an iterator adaptor that flattens an iterator of iterators by
|
||||
/// merging them in ascending order.
|
||||
///
|
||||
|
@ -621,10 +733,11 @@ pub trait Itertools : Iterator {
|
|||
/// let it = vec![a, b, c].into_iter().kmerge();
|
||||
/// itertools::assert_equal(it, vec![0, 1, 2, 3, 4, 5]);
|
||||
/// ```
|
||||
fn kmerge(self) -> KMerge<<<Self as Iterator>::Item as IntoIterator>::IntoIter>
|
||||
#[cfg(feature = "use_std")]
|
||||
fn kmerge(self) -> KMerge<<Self::Item as IntoIterator>::IntoIter>
|
||||
where Self: Sized,
|
||||
Self::Item: IntoIterator,
|
||||
<<Self as Iterator>::Item as IntoIterator>::Item: PartialOrd,
|
||||
<Self::Item as IntoIterator>::Item: PartialOrd,
|
||||
{
|
||||
kmerge(self)
|
||||
}
|
||||
|
@ -649,12 +762,13 @@ pub trait Itertools : Iterator {
|
|||
/// assert_eq!(it.next(), Some(0.));
|
||||
/// assert_eq!(it.last(), Some(-7.));
|
||||
/// ```
|
||||
#[cfg(feature = "use_std")]
|
||||
fn kmerge_by<F>(self, first: F)
|
||||
-> KMergeBy<<<Self as Iterator>::Item as IntoIterator>::IntoIter, F>
|
||||
-> KMergeBy<<Self::Item as IntoIterator>::IntoIter, F>
|
||||
where Self: Sized,
|
||||
Self::Item: IntoIterator,
|
||||
F: FnMut(&<<Self as Iterator>::Item as IntoIterator>::Item,
|
||||
&<<Self as Iterator>::Item as IntoIterator>::Item) -> bool
|
||||
F: FnMut(&<Self::Item as IntoIterator>::Item,
|
||||
&<Self::Item as IntoIterator>::Item) -> bool
|
||||
{
|
||||
kmerge_by(self, first)
|
||||
}
|
||||
|
@ -679,14 +793,51 @@ pub trait Itertools : Iterator {
|
|||
adaptors::cartesian_product(self, other.into_iter())
|
||||
}
|
||||
|
||||
/// Return an iterator adaptor that iterates over the cartesian product of
|
||||
/// all subiterators returned by meta-iterator `self`.
|
||||
///
|
||||
/// All provided iterators must yield the same `Item` type. To generate
|
||||
/// the product of iterators yielding multiple types, use the
|
||||
/// [`iproduct`](macro.iproduct.html) macro instead.
|
||||
///
|
||||
///
|
||||
/// The iterator element type is `Vec<T>`, where `T` is the iterator element
|
||||
/// of the subiterators.
|
||||
///
|
||||
/// ```
|
||||
/// use itertools::Itertools;
|
||||
/// let mut multi_prod = (0..3).map(|i| (i * 2)..(i * 2 + 2))
|
||||
/// .multi_cartesian_product();
|
||||
/// assert_eq!(multi_prod.next(), Some(vec![0, 2, 4]));
|
||||
/// assert_eq!(multi_prod.next(), Some(vec![0, 2, 5]));
|
||||
/// assert_eq!(multi_prod.next(), Some(vec![0, 3, 4]));
|
||||
/// assert_eq!(multi_prod.next(), Some(vec![0, 3, 5]));
|
||||
/// assert_eq!(multi_prod.next(), Some(vec![1, 2, 4]));
|
||||
/// assert_eq!(multi_prod.next(), Some(vec![1, 2, 5]));
|
||||
/// assert_eq!(multi_prod.next(), Some(vec![1, 3, 4]));
|
||||
/// assert_eq!(multi_prod.next(), Some(vec![1, 3, 5]));
|
||||
/// assert_eq!(multi_prod.next(), None);
|
||||
/// ```
|
||||
#[cfg(feature = "use_std")]
|
||||
fn multi_cartesian_product(self) -> MultiProduct<<Self::Item as IntoIterator>::IntoIter>
|
||||
where Self: Iterator + Sized,
|
||||
Self::Item: IntoIterator,
|
||||
<Self::Item as IntoIterator>::IntoIter: Clone,
|
||||
<Self::Item as IntoIterator>::Item: Clone
|
||||
{
|
||||
adaptors::multi_cartesian_product(self)
|
||||
}
|
||||
|
||||
/// Return an iterator adaptor that uses the passed-in closure to
|
||||
/// optionally merge together consecutive elements.
|
||||
///
|
||||
/// The closure `f` is passed two elements, `x`, `y` and may return either
|
||||
/// (1) `Ok(z)` to merge the two values or (2) `Err((x', y'))` to indicate
|
||||
/// they can't be merged. In (2), the value `x'` is emitted by the iterator.
|
||||
/// Coalesce continues with either `z` (1) or `y'` (2), and the next
|
||||
/// iterator element as the next pair of elements to merge.
|
||||
/// The closure `f` is passed two elements, `previous` and `current` and may
|
||||
/// return either (1) `Ok(combined)` to merge the two values or
|
||||
/// (2) `Err((previous', current'))` to indicate they can't be merged.
|
||||
/// In (2), the value `previous'` is emitted by the iterator.
|
||||
/// Either (1) `combined` or (2) `current'` becomes the previous value
|
||||
/// when coalesce continues with the next pair of elements to merge. The
|
||||
/// value that remains at the end is also emitted by the iterator.
|
||||
///
|
||||
/// Iterator element type is `Self::Item`.
|
||||
///
|
||||
|
@ -748,11 +899,12 @@ pub trait Itertools : Iterator {
|
|||
/// itertools::assert_equal(data.into_iter().unique(),
|
||||
/// vec![10, 20, 30, 40, 50]);
|
||||
/// ```
|
||||
#[cfg(feature = "use_std")]
|
||||
fn unique(self) -> Unique<Self>
|
||||
where Self: Sized,
|
||||
Self::Item: Clone + Eq + Hash
|
||||
{
|
||||
adaptors::unique(self)
|
||||
unique_impl::unique(self)
|
||||
}
|
||||
|
||||
/// Return an iterator adaptor that filters out elements that have
|
||||
|
@ -769,12 +921,13 @@ pub trait Itertools : Iterator {
|
|||
/// itertools::assert_equal(data.into_iter().unique_by(|s| s.len()),
|
||||
/// vec!["a", "bb", "ccc"]);
|
||||
/// ```
|
||||
#[cfg(feature = "use_std")]
|
||||
fn unique_by<V, F>(self, f: F) -> UniqueBy<Self, V, F>
|
||||
where Self: Sized,
|
||||
V: Eq + Hash,
|
||||
F: FnMut(&Self::Item) -> V
|
||||
{
|
||||
adaptors::unique_by(self, f)
|
||||
unique_impl::unique_by(self, f)
|
||||
}
|
||||
|
||||
/// Return an iterator adaptor that borrows from this iterator and
|
||||
|
@ -888,8 +1041,6 @@ pub trait Itertools : Iterator {
|
|||
/// Iterator element type is `Vec<Self::Item>`. The iterator produces a new Vec per iteration,
|
||||
/// and clones the iterator elements.
|
||||
///
|
||||
/// **Panics** if `n` is zero.
|
||||
///
|
||||
/// ```
|
||||
/// use itertools::Itertools;
|
||||
///
|
||||
|
@ -901,11 +1052,12 @@ pub trait Itertools : Iterator {
|
|||
/// vec![2, 3, 4],
|
||||
/// ]);
|
||||
/// ```
|
||||
#[cfg(feature = "use_std")]
|
||||
fn combinations(self, n: usize) -> Combinations<Self>
|
||||
where Self: Sized,
|
||||
Self::Item: Clone
|
||||
{
|
||||
adaptors::combinations(self, n)
|
||||
combinations::combinations(self, n)
|
||||
}
|
||||
|
||||
/// Return an iterator adaptor that pads the sequence to a minimum length of
|
||||
|
@ -934,17 +1086,18 @@ pub trait Itertools : Iterator {
|
|||
|
||||
/// Unravel a nested iterator.
|
||||
///
|
||||
/// This is a shortcut for `it.flat_map(|x| x)`.
|
||||
/// This is more or less equivalent to `.flat_map` with an identity
|
||||
/// function.
|
||||
///
|
||||
/// ```
|
||||
/// use itertools::Itertools;
|
||||
///
|
||||
/// let data = vec![vec![1, 2, 3], vec![4, 5, 6]];
|
||||
/// let flattened = data.into_iter().flatten();
|
||||
/// let flattened = data.iter().flatten();
|
||||
///
|
||||
/// itertools::assert_equal(flattened, vec![1, 2, 3, 4, 5, 6]);
|
||||
/// itertools::assert_equal(flattened, &[1, 2, 3, 4, 5, 6]);
|
||||
/// ```
|
||||
fn flatten(self) -> Flatten<Self, <<Self as Iterator>::Item as IntoIterator>::IntoIter>
|
||||
fn flatten(self) -> Flatten<Self, <Self::Item as IntoIterator>::IntoIter>
|
||||
where Self: Sized,
|
||||
Self::Item: IntoIterator
|
||||
{
|
||||
|
@ -976,6 +1129,43 @@ pub trait Itertools : Iterator {
|
|||
with_position::with_position(self)
|
||||
}
|
||||
|
||||
/// Return an iterator adaptor that yields the indices of all elements
|
||||
/// satisfying a predicate, counted from the start of the iterator.
|
||||
///
|
||||
/// Equivalent to `iter.enumerate().filter(|(_, v)| predicate(v)).map(|(i, _)| i)`.
|
||||
///
|
||||
/// ```
|
||||
/// use itertools::Itertools;
|
||||
///
|
||||
/// let data = vec![1, 2, 3, 3, 4, 6, 7, 9];
|
||||
/// itertools::assert_equal(data.iter().positions(|v| v % 2 == 0), vec![1, 4, 5]);
|
||||
///
|
||||
/// itertools::assert_equal(data.iter().positions(|v| v % 2 == 1).rev(), vec![7, 6, 3, 2, 0]);
|
||||
/// ```
|
||||
fn positions<P>(self, predicate: P) -> Positions<Self, P>
|
||||
where Self: Sized,
|
||||
P: FnMut(Self::Item) -> bool,
|
||||
{
|
||||
adaptors::positions(self, predicate)
|
||||
}
|
||||
|
||||
/// Return an iterator adaptor that applies a mutating function
|
||||
/// to each element before yielding it.
|
||||
///
|
||||
/// ```
|
||||
/// use itertools::Itertools;
|
||||
///
|
||||
/// let input = vec![vec![1], vec![3, 2, 1]];
|
||||
/// let it = input.into_iter().update(|mut v| v.push(0));
|
||||
/// itertools::assert_equal(it, vec![vec![1, 0], vec![3, 2, 1, 0]]);
|
||||
/// ```
|
||||
fn update<F>(self, updater: F) -> Update<Self, F>
|
||||
where Self: Sized,
|
||||
F: FnMut(&mut Self::Item),
|
||||
{
|
||||
adaptors::update(self, updater)
|
||||
}
|
||||
|
||||
// non-adaptor methods
|
||||
/// Advances the iterator and returns the next items grouped in a tuple of
|
||||
/// a specific size (up to 4).
|
||||
|
@ -997,6 +1187,37 @@ pub trait Itertools : Iterator {
|
|||
T::collect_from_iter_no_buf(self)
|
||||
}
|
||||
|
||||
/// Collects all items from the iterator into a tuple of a specific size
|
||||
/// (up to 4).
|
||||
///
|
||||
/// If the number of elements inside the iterator is **exactly** equal to
|
||||
/// the tuple size, then the tuple is returned inside `Some`, otherwise
|
||||
/// `None` is returned.
|
||||
///
|
||||
/// ```
|
||||
/// use itertools::Itertools;
|
||||
///
|
||||
/// let iter = 1..3;
|
||||
///
|
||||
/// if let Some((x, y)) = iter.collect_tuple() {
|
||||
/// assert_eq!((x, y), (1, 2))
|
||||
/// } else {
|
||||
/// panic!("Expected two elements")
|
||||
/// }
|
||||
/// ```
|
||||
fn collect_tuple<T>(mut self) -> Option<T>
|
||||
where Self: Sized + Iterator<Item = T::Item>,
|
||||
T: tuple_impl::TupleCollect
|
||||
{
|
||||
match self.next_tuple() {
|
||||
elt @ Some(_) => match self.next() {
|
||||
Some(_) => None,
|
||||
None => elt,
|
||||
},
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Find the position and value of the first element satisfying a predicate.
|
||||
///
|
||||
|
@ -1021,6 +1242,28 @@ pub trait Itertools : Iterator {
|
|||
None
|
||||
}
|
||||
|
||||
/// Check whether all elements compare equal.
|
||||
///
|
||||
/// Empty iterators are considered to have equal elements:
|
||||
///
|
||||
/// ```
|
||||
/// use itertools::Itertools;
|
||||
///
|
||||
/// let data = vec![1, 1, 1, 2, 2, 3, 3, 3, 4, 5, 5];
|
||||
/// assert!(!data.iter().all_equal());
|
||||
/// assert!(data[0..3].iter().all_equal());
|
||||
/// assert!(data[3..5].iter().all_equal());
|
||||
/// assert!(data[5..8].iter().all_equal());
|
||||
///
|
||||
/// let data : Option<usize> = None;
|
||||
/// assert!(data.into_iter().all_equal());
|
||||
/// ```
|
||||
fn all_equal(&mut self) -> bool
|
||||
where Self::Item: PartialEq,
|
||||
{
|
||||
self.dedup().nth(1).is_none()
|
||||
}
|
||||
|
||||
/// Consume the first `n` elements from the iterator eagerly,
|
||||
/// and return the same iterator again.
|
||||
///
|
||||
|
@ -1087,16 +1330,36 @@ pub trait Itertools : Iterator {
|
|||
///
|
||||
/// itertools::assert_equal(rx.iter(), vec![1, 3, 5, 7, 9]);
|
||||
/// ```
|
||||
fn foreach<F>(&mut self, mut f: F)
|
||||
where F: FnMut(Self::Item)
|
||||
fn foreach<F>(self, mut f: F)
|
||||
where F: FnMut(Self::Item),
|
||||
Self: Sized,
|
||||
{
|
||||
// FIXME: This use of fold doesn't actually do any iterator
|
||||
// specific traversal (fold requries `self`)
|
||||
self.fold((), move |(), element| f(element))
|
||||
}
|
||||
|
||||
/// Combine all an iterator's elements into one element by using `Extend`.
|
||||
///
|
||||
/// This combinator will extend the first item with each of the rest of the
|
||||
/// items of the iterator. If the iterator is empty, the default value of
|
||||
/// `I::Item` is returned.
|
||||
///
|
||||
/// ```rust
|
||||
/// use itertools::Itertools;
|
||||
///
|
||||
/// let input = vec![vec![1], vec![2, 3], vec![4, 5, 6]];
|
||||
/// assert_eq!(input.into_iter().concat(),
|
||||
/// vec![1, 2, 3, 4, 5, 6]);
|
||||
/// ```
|
||||
fn concat(self) -> Self::Item
|
||||
where Self: Sized,
|
||||
Self::Item: Extend<<<Self as Iterator>::Item as IntoIterator>::Item> + IntoIterator + Default
|
||||
{
|
||||
concat(self)
|
||||
}
|
||||
|
||||
/// `.collect_vec()` is simply a type specialization of `.collect()`,
|
||||
/// for convenience.
|
||||
#[cfg(feature = "use_std")]
|
||||
fn collect_vec(self) -> Vec<Self::Item>
|
||||
where Self: Sized
|
||||
{
|
||||
|
@ -1144,6 +1407,7 @@ pub trait Itertools : Iterator {
|
|||
/// assert_eq!(["a", "b", "c"].iter().join(", "), "a, b, c");
|
||||
/// assert_eq!([1, 2, 3].iter().join(", "), "1, 2, 3");
|
||||
/// ```
|
||||
#[cfg(feature = "use_std")]
|
||||
fn join(&mut self, sep: &str) -> String
|
||||
where Self::Item: std::fmt::Display
|
||||
{
|
||||
|
@ -1184,14 +1448,6 @@ pub trait Itertools : Iterator {
|
|||
format::new_format_default(self, sep)
|
||||
}
|
||||
|
||||
///
|
||||
#[deprecated(note = "renamed to .format()")]
|
||||
fn format_default(self, sep: &str) -> Format<Self>
|
||||
where Self: Sized,
|
||||
{
|
||||
format::new_format_default(self, sep)
|
||||
}
|
||||
|
||||
/// Format all iterator elements, separated by `sep`.
|
||||
///
|
||||
/// This is a customizable version of `.format()`.
|
||||
|
@ -1330,18 +1586,11 @@ 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);
|
||||
/// ```
|
||||
fn fold1<F>(&mut self, mut f: F) -> Option<Self::Item>
|
||||
where F: FnMut(Self::Item, Self::Item) -> Self::Item
|
||||
fn fold1<F>(mut self, f: F) -> Option<Self::Item>
|
||||
where F: FnMut(Self::Item, Self::Item) -> Self::Item,
|
||||
Self: Sized,
|
||||
{
|
||||
match self.next() {
|
||||
None => None,
|
||||
Some(mut x) => {
|
||||
for y in self {
|
||||
x = f(x, y);
|
||||
}
|
||||
Some(x)
|
||||
}
|
||||
}
|
||||
self.next().map(move |x| self.fold(x, f))
|
||||
}
|
||||
|
||||
/// An iterator method that applies a function, producing a single, final value.
|
||||
|
@ -1373,7 +1622,7 @@ pub trait Itertools : Iterator {
|
|||
/// // fold_while:
|
||||
/// let result3 = numbers.iter().fold_while(0, |acc, x| {
|
||||
/// if *x > 5 { Done(acc) } else { Continue(acc + x) }
|
||||
/// });
|
||||
/// }).into_inner();
|
||||
///
|
||||
/// // they're the same
|
||||
/// assert_eq!(result, result2);
|
||||
|
@ -1383,23 +1632,18 @@ pub trait Itertools : Iterator {
|
|||
/// The big difference between the computations of `result2` and `result3` is that while
|
||||
/// `fold()` called the provided closure for every item of the callee iterator,
|
||||
/// `fold_while()` actually stopped iterating as soon as it encountered `Fold::Done(_)`.
|
||||
fn fold_while<B, F>(self, init: B, mut f: F) -> B
|
||||
fn fold_while<B, F>(&mut self, init: B, mut f: F) -> FoldWhile<B>
|
||||
where Self: Sized,
|
||||
F: FnMut(B, Self::Item) -> FoldWhile<B>
|
||||
{
|
||||
let mut accum = init;
|
||||
for item in self {
|
||||
match f(accum, item) {
|
||||
FoldWhile::Continue(res) => {
|
||||
accum = res;
|
||||
}
|
||||
FoldWhile::Done(res) => {
|
||||
accum = res;
|
||||
break;
|
||||
let mut acc = init;
|
||||
while let Some(item) = self.next() {
|
||||
match f(acc, item) {
|
||||
FoldWhile::Continue(res) => acc = res,
|
||||
res @ FoldWhile::Done(_) => return res,
|
||||
}
|
||||
}
|
||||
}
|
||||
accum
|
||||
FoldWhile::Continue(acc)
|
||||
}
|
||||
|
||||
/// Collect all iterator elements into a sorted vector in ascending order.
|
||||
|
@ -1415,6 +1659,7 @@ pub trait Itertools : Iterator {
|
|||
/// itertools::assert_equal(text.chars().sorted(),
|
||||
/// "abcdef".chars());
|
||||
/// ```
|
||||
#[cfg(feature = "use_std")]
|
||||
fn sorted(self) -> Vec<Self::Item>
|
||||
where Self: Sized,
|
||||
Self::Item: Ord
|
||||
|
@ -1442,6 +1687,7 @@ pub trait Itertools : Iterator {
|
|||
/// itertools::assert_equal(oldest_people_first,
|
||||
/// vec!["Jill", "Jack", "Jane", "John"]);
|
||||
/// ```
|
||||
#[cfg(feature = "use_std")]
|
||||
fn sorted_by<F>(self, cmp: F) -> Vec<Self::Item>
|
||||
where Self: Sized,
|
||||
F: FnMut(&Self::Item, &Self::Item) -> Ordering,
|
||||
|
@ -1452,6 +1698,38 @@ pub trait Itertools : Iterator {
|
|||
v
|
||||
}
|
||||
|
||||
/// Collect all iterator elements into a sorted vector.
|
||||
///
|
||||
/// **Note:** This consumes the entire iterator, uses the
|
||||
/// `slice::sort_by_key()` method and returns the sorted vector.
|
||||
///
|
||||
/// ```
|
||||
/// 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_key(|x| -x.1)
|
||||
/// .into_iter()
|
||||
/// .map(|(person, _age)| person);
|
||||
///
|
||||
/// itertools::assert_equal(oldest_people_first,
|
||||
/// vec!["Jill", "Jack", "Jane", "John"]);
|
||||
/// ```
|
||||
#[cfg(feature = "use_std")]
|
||||
fn sorted_by_key<K, F>(self, f: F) -> Vec<Self::Item>
|
||||
where Self: Sized,
|
||||
K: Ord,
|
||||
F: FnMut(&Self::Item) -> K,
|
||||
{
|
||||
let mut v: Vec<Self::Item> = self.collect();
|
||||
|
||||
v.sort_by_key(f);
|
||||
v
|
||||
}
|
||||
|
||||
/// Collect all iterator elements into one of two
|
||||
/// partitions. Unlike `Iterator::partition`, each partition may
|
||||
/// have a distinct type.
|
||||
|
@ -1571,12 +1849,12 @@ pub trait Itertools : Iterator {
|
|||
|
||||
impl<T: ?Sized> Itertools for T where T: Iterator { }
|
||||
|
||||
/// Return `true` if both iterators produce equal sequences
|
||||
/// Return `true` if both iterables produce equal sequences
|
||||
/// (elements pairwise equal and sequences of the same length),
|
||||
/// `false` otherwise.
|
||||
///
|
||||
/// **Note:** the standard library method `Iterator::eq` now provides
|
||||
/// the same functionality.
|
||||
/// This is an `IntoIterator` enabled function that is similar to the standard
|
||||
/// library method `Iterator::eq`.
|
||||
///
|
||||
/// ```
|
||||
/// assert!(itertools::equal(vec![1, 2, 3], 1..4));
|
||||
|
@ -1591,8 +1869,8 @@ pub fn equal<I, J>(a: I, b: J) -> bool
|
|||
let mut ib = b.into_iter();
|
||||
loop {
|
||||
match ia.next() {
|
||||
Some(ref x) => match ib.next() {
|
||||
Some(ref y) => if x != y { return false; },
|
||||
Some(x) => match ib.next() {
|
||||
Some(y) => if x != y { return false; },
|
||||
None => return false,
|
||||
},
|
||||
None => return ib.next().is_none()
|
||||
|
@ -1600,7 +1878,7 @@ pub fn equal<I, J>(a: I, b: J) -> bool
|
|||
}
|
||||
}
|
||||
|
||||
/// Assert that two iterators produce equal sequences, with the same
|
||||
/// Assert that two iterables produce equal sequences, with the same
|
||||
/// semantics as *equal(a, b)*.
|
||||
///
|
||||
/// **Panics** on assertion failure with a message that shows the
|
||||
|
@ -1679,6 +1957,7 @@ pub fn partition<'a, A: 'a, I, F>(iter: I, mut pred: F) -> usize
|
|||
/// An enum used for controlling the execution of `.fold_while()`.
|
||||
///
|
||||
/// See [`.fold_while()`](trait.Itertools.html#method.fold_while) for more information.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum FoldWhile<T> {
|
||||
/// Continue folding with this value
|
||||
Continue(T),
|
||||
|
@ -1686,3 +1965,19 @@ pub enum FoldWhile<T> {
|
|||
Done(T),
|
||||
}
|
||||
|
||||
impl<T> FoldWhile<T> {
|
||||
/// Return the value in the continue or done.
|
||||
pub fn into_inner(self) -> T {
|
||||
match self {
|
||||
FoldWhile::Continue(x) | FoldWhile::Done(x) => x,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return true if `self` is `Done`, false if it is `Continue`.
|
||||
pub fn is_done(&self) -> bool {
|
||||
match *self {
|
||||
FoldWhile::Continue(_) => false,
|
||||
FoldWhile::Done(_) => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
use std::cmp::Ordering;
|
||||
use std::iter::Fuse;
|
||||
|
||||
use super::adaptors::{PutBack, put_back};
|
||||
use either_or_both::EitherOrBoth;
|
||||
|
||||
/// Return an iterator adaptor that merge-joins items from the two base iterators in ascending order.
|
||||
///
|
||||
/// See [`.merge_join_by()`](trait.Itertools.html#method.merge_join_by) for more information.
|
||||
pub fn merge_join_by<I, J, F>(left: I, right: J, cmp_fn: F)
|
||||
-> MergeJoinBy<I::IntoIter, J::IntoIter, F>
|
||||
where I: IntoIterator,
|
||||
J: IntoIterator,
|
||||
F: FnMut(&I::Item, &J::Item) -> Ordering
|
||||
{
|
||||
MergeJoinBy {
|
||||
left: put_back(left.into_iter().fuse()),
|
||||
right: put_back(right.into_iter().fuse()),
|
||||
cmp_fn: cmp_fn
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator adaptor that merge-joins items from the two base iterators in ascending order.
|
||||
///
|
||||
/// See [`.merge_join_by()`](../trait.Itertools.html#method.merge_join_by) for more information.
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct MergeJoinBy<I: Iterator, J: Iterator, F> {
|
||||
left: PutBack<Fuse<I>>,
|
||||
right: PutBack<Fuse<J>>,
|
||||
cmp_fn: F
|
||||
}
|
||||
|
||||
impl<I, J, F> Iterator for MergeJoinBy<I, J, F>
|
||||
where I: Iterator,
|
||||
J: Iterator,
|
||||
F: FnMut(&I::Item, &J::Item) -> Ordering
|
||||
{
|
||||
type Item = EitherOrBoth<I::Item, J::Item>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match (self.left.next(), self.right.next()) {
|
||||
(None, None) => None,
|
||||
(Some(left), None) =>
|
||||
Some(EitherOrBoth::Left(left)),
|
||||
(None, Some(right)) =>
|
||||
Some(EitherOrBoth::Right(right)),
|
||||
(Some(left), Some(right)) => {
|
||||
match (self.cmp_fn)(&left, &right) {
|
||||
Ordering::Equal =>
|
||||
Some(EitherOrBoth::Both(left, right)),
|
||||
Ordering::Less => {
|
||||
self.right.put_back(right);
|
||||
Some(EitherOrBoth::Left(left))
|
||||
},
|
||||
Ordering::Greater => {
|
||||
self.left.put_back(left);
|
||||
Some(EitherOrBoth::Right(right))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let (a_lower, a_upper) = self.left.size_hint();
|
||||
let (b_lower, b_upper) = self.right.size_hint();
|
||||
|
||||
let lower = ::std::cmp::max(a_lower, b_lower);
|
||||
|
||||
let upper = match (a_upper, b_upper) {
|
||||
(Some(x), Some(y)) => Some(x + y),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
(lower, upper)
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
use std::iter::Fuse;
|
||||
use std::collections::VecDeque;
|
||||
use size_hint;
|
||||
use PeekingNext;
|
||||
|
||||
/// See [`multipeek()`](../fn.multipeek.html) for more information.
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -57,6 +58,25 @@ impl<I: Iterator> MultiPeek<I> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<I> PeekingNext for MultiPeek<I>
|
||||
where I: Iterator,
|
||||
{
|
||||
fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
|
||||
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.get(0) {
|
||||
if !accept(r) { return None }
|
||||
}
|
||||
}
|
||||
self.next()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> Iterator for MultiPeek<I>
|
||||
where I: Iterator
|
||||
{
|
|
@ -8,6 +8,7 @@ use size_hint;
|
|||
///
|
||||
/// See [`.pad_using()`](../trait.Itertools.html#method.pad_using) for more information.
|
||||
#[derive(Clone)]
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct PadUsing<I, F> {
|
||||
iter: Fuse<I>,
|
||||
min: usize,
|
||||
|
@ -77,6 +78,6 @@ impl<I, F> DoubleEndedIterator for PadUsing<I, F>
|
|||
}
|
||||
|
||||
impl<I, F> ExactSizeIterator for PadUsing<I, F>
|
||||
where I: Iterator,
|
||||
where I: ExactSizeIterator,
|
||||
F: FnMut(usize) -> I::Item
|
||||
{}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
use std::iter::Peekable;
|
||||
use PutBack;
|
||||
#[cfg(feature = "use_std")]
|
||||
use PutBackN;
|
||||
|
||||
/// An iterator that allows peeking at an element before deciding to accept it.
|
||||
|
@ -52,6 +53,7 @@ impl<I> PeekingNext for PutBack<I>
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "use_std")]
|
||||
impl<I> PeekingNext for PutBackN<I>
|
||||
where I: Iterator,
|
||||
{
|
||||
|
@ -74,6 +76,7 @@ impl<I> PeekingNext for PutBackN<I>
|
|||
///
|
||||
/// See [`.peeking_take_while()`](../trait.Itertools.html#method.peeking_take_while)
|
||||
/// for more information.
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct PeekingTakeWhile<'a, I: 'a, F>
|
||||
where I: Iterator,
|
||||
{
|
||||
|
@ -110,10 +113,7 @@ impl<'a, I, F> Iterator for PeekingTakeWhile<'a, I, F>
|
|||
// Some iterators are so lightweight we can simply clone them to save their
|
||||
// state and use that for peeking.
|
||||
macro_rules! peeking_next_by_clone {
|
||||
(@as_item $x:item) => ($x);
|
||||
([$($typarm:tt)*] $type_:ty) => {
|
||||
// FIXME: Ast coercion is dead as soon as we can dep on Rust 1.12
|
||||
peeking_next_by_clone! { @as_item
|
||||
impl<$($typarm)*> PeekingNext for $type_ {
|
||||
fn peeking_next<F>(&mut self, accept: F) -> Option<Self::Item>
|
||||
where F: FnOnce(&Self::Item) -> bool
|
||||
|
@ -130,7 +130,6 @@ macro_rules! peeking_next_by_clone {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
peeking_next_by_clone! { ['a, T] ::std::slice::Iter<'a, T> }
|
||||
|
@ -140,7 +139,9 @@ peeking_next_by_clone! { ['a] ::std::str::Bytes<'a> }
|
|||
peeking_next_by_clone! { ['a, T] ::std::option::Iter<'a, T> }
|
||||
peeking_next_by_clone! { ['a, T] ::std::result::Iter<'a, T> }
|
||||
peeking_next_by_clone! { [T] ::std::iter::Empty<T> }
|
||||
#[cfg(feature = "use_std")]
|
||||
peeking_next_by_clone! { ['a, T] ::std::collections::linked_list::Iter<'a, T> }
|
||||
#[cfg(feature = "use_std")]
|
||||
peeking_next_by_clone! { ['a, T] ::std::collections::vec_deque::Iter<'a, T> }
|
||||
|
||||
// cloning a Rev has no extra overhead; peekable and put backs are never DEI.
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
|
||||
/// An iterator that produces only the `T` values as long as the
|
||||
/// inner iterator produces `Ok(T)`.
|
||||
///
|
||||
/// Used by [`process_results`](../fn.process_results.html), see its docs
|
||||
/// for more information.
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct ProcessResults<'a, I, E: 'a> {
|
||||
error: &'a mut Result<(), E>,
|
||||
iter: I,
|
||||
}
|
||||
|
||||
impl<'a, I, T, E> Iterator for ProcessResults<'a, I, E>
|
||||
where I: Iterator<Item = Result<T, E>>
|
||||
{
|
||||
type Item = T;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self.iter.next() {
|
||||
Some(Ok(x)) => Some(x),
|
||||
Some(Err(e)) => {
|
||||
*self.error = Err(e);
|
||||
None
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let (_, hi) = self.iter.size_hint();
|
||||
(0, hi)
|
||||
}
|
||||
}
|
||||
|
||||
/// “Lift” a function of the values of an iterator so that it can process
|
||||
/// an iterator of `Result` values instead.
|
||||
///
|
||||
/// `iterable` is an iterator or iterable with `Result<T, E>` elements, where
|
||||
/// `T` is the value type and `E` the error type.
|
||||
///
|
||||
/// `processor` is a closure that receives an adapted version of the iterable
|
||||
/// as the only argument — the adapted iterator produces elements of type `T`,
|
||||
/// as long as the original iterator produces `Ok` values.
|
||||
///
|
||||
/// If the original iterable produces an error at any point, the adapted
|
||||
/// iterator ends and the `process_results` function will return the
|
||||
/// error iself.
|
||||
///
|
||||
/// Otherwise, the return value from the closure is returned wrapped
|
||||
/// inside `Ok`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use itertools::process_results;
|
||||
///
|
||||
/// type R = Result<i32, &'static str>;
|
||||
///
|
||||
/// let first_values: Vec<R> = vec![Ok(1), Ok(0), Ok(3)];
|
||||
/// let second_values: Vec<R> = vec![Ok(2), Ok(1), Err("overflow")];
|
||||
///
|
||||
/// // “Lift” the iterator .max() method to work on the values in Results using process_results
|
||||
///
|
||||
/// let first_max = process_results(first_values, |iter| iter.max().unwrap_or(0));
|
||||
/// let second_max = process_results(second_values, |iter| iter.max().unwrap_or(0));
|
||||
///
|
||||
/// assert_eq!(first_max, Ok(3));
|
||||
/// assert!(second_max.is_err());
|
||||
/// ```
|
||||
pub fn process_results<I, F, T, E, R>(iterable: I, processor: F) -> Result<R, E>
|
||||
where I: IntoIterator<Item = Result<T, E>>,
|
||||
F: FnOnce(ProcessResults<I::IntoIter, E>) -> R
|
||||
{
|
||||
let iter = iterable.into_iter();
|
||||
let mut error = Ok(());
|
||||
|
||||
let result = processor(ProcessResults { error: &mut error, iter: iter });
|
||||
|
||||
error.map(|_| result)
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
use size_hint;
|
||||
|
||||
/// An iterator adaptor that allows putting multiple
|
||||
/// items in front of the iterator.
|
||||
///
|
||||
/// Iterator element type is `I::Item`.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PutBackN<I: Iterator> {
|
||||
top: Vec<I::Item>,
|
||||
iter: I,
|
||||
}
|
||||
|
||||
/// Create an iterator where you can put back multiple values to the front
|
||||
/// of the iteration.
|
||||
///
|
||||
/// Iterator element type is `I::Item`.
|
||||
pub fn put_back_n<I>(iterable: I) -> PutBackN<I::IntoIter>
|
||||
where I: IntoIterator
|
||||
{
|
||||
PutBackN {
|
||||
top: Vec::new(),
|
||||
iter: iterable.into_iter(),
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Iterator> PutBackN<I> {
|
||||
/// Puts x in front of the iterator.
|
||||
/// The values are yielded in order of the most recently put back
|
||||
/// values first.
|
||||
///
|
||||
/// ```rust
|
||||
/// use itertools::put_back_n;
|
||||
///
|
||||
/// let mut it = put_back_n(1..5);
|
||||
/// it.next();
|
||||
/// it.put_back(1);
|
||||
/// it.put_back(0);
|
||||
///
|
||||
/// assert!(itertools::equal(it, 0..5));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn put_back(&mut self, x: I::Item) {
|
||||
self.top.push(x);
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Iterator> Iterator for PutBackN<I> {
|
||||
type Item = I::Item;
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<I::Item> {
|
||||
if self.top.is_empty() {
|
||||
self.iter.next()
|
||||
} else {
|
||||
self.top.pop()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
size_hint::add_scalar(self.iter.size_hint(), self.top.len())
|
||||
}
|
||||
}
|
||||
|
|
@ -15,27 +15,34 @@ pub struct RcIter<I> {
|
|||
/// same original iterator.
|
||||
///
|
||||
/// `RcIter` allows doing interesting things like using `.zip()` on an iterator with
|
||||
/// itself, at the cost of runtime borrow checking.
|
||||
/// (If it is not obvious: this has a performance penalty.)
|
||||
/// itself, at the cost of runtime borrow checking which may have a performance
|
||||
/// penalty.
|
||||
///
|
||||
/// Iterator element type is `Self::Item`.
|
||||
///
|
||||
/// ```
|
||||
/// use itertools::rciter;
|
||||
/// use itertools::zip;
|
||||
///
|
||||
/// // In this example a range iterator is created and we iterate it using
|
||||
/// // three separate handles (two of them given to zip).
|
||||
/// // We also use the IntoIterator implementation for `&RcIter`.
|
||||
///
|
||||
/// let mut iter = rciter(0..9);
|
||||
/// let mut z = zip(&iter, &iter);
|
||||
///
|
||||
/// let mut rit = rciter(0..9);
|
||||
/// let mut z = rit.clone().zip(rit.clone());
|
||||
/// assert_eq!(z.next(), Some((0, 1)));
|
||||
/// assert_eq!(z.next(), Some((2, 3)));
|
||||
/// assert_eq!(z.next(), Some((4, 5)));
|
||||
/// assert_eq!(rit.next(), Some(6));
|
||||
/// assert_eq!(iter.next(), Some(6));
|
||||
/// assert_eq!(z.next(), Some((7, 8)));
|
||||
/// assert_eq!(z.next(), None);
|
||||
/// ```
|
||||
///
|
||||
/// **Panics** in iterator methods if a borrow error is encountered,
|
||||
/// but it can only happen if the `RcIter` is reentered in for example `.next()`,
|
||||
/// i.e. if it somehow participates in an “iterator knot” where it is an adaptor of itself.
|
||||
/// **Panics** in iterator methods if a borrow error is encountered in the
|
||||
/// iterator methods. It can only happen if the `RcIter` is reentered in
|
||||
/// `.next()`, i.e. if it somehow participates in an “iterator knot”
|
||||
/// where it is an adaptor of itself.
|
||||
pub fn rciter<I>(iterable: I) -> RcIter<I::IntoIter>
|
||||
where I: IntoIterator
|
||||
{
|
||||
|
@ -76,6 +83,7 @@ impl<I> DoubleEndedIterator for RcIter<I>
|
|||
self.rciter.borrow_mut().next_back()
|
||||
}
|
||||
}
|
||||
|
||||
/// Return an iterator from `&RcIter<I>` (by simply cloning it).
|
||||
impl<'a, I> IntoIterator for &'a RcIter<I>
|
||||
where I: Iterator
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
/// An iterator that produces *n* repetitions of an element.
|
||||
///
|
||||
/// See [`repeat_n()`](../fn.repeat_n.html) for more information.
|
||||
#[must_use = "iterators are lazy and do nothing unless consumed"]
|
||||
pub struct RepeatN<A> {
|
||||
elt: Option<A>,
|
||||
n: usize,
|
||||
|
@ -18,23 +19,6 @@ pub fn repeat_n<A>(element: A, n: usize) -> RepeatN<A>
|
|||
}
|
||||
}
|
||||
|
||||
impl<A> RepeatN<A> {
|
||||
#[deprecated(note = "The ::new constructor is deprecated. Use `repeat_n`")]
|
||||
///
|
||||
pub fn new(elt: A, n: usize) -> Self {
|
||||
// The code is duplicated here because the new version uses
|
||||
// the proper A: Clone bound.
|
||||
if n == 0 {
|
||||
RepeatN { elt: None, n: n }
|
||||
} else {
|
||||
RepeatN {
|
||||
elt: Some(elt),
|
||||
n: n,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> Iterator for RepeatN<A>
|
||||
where A: Clone
|
||||
{
|
||||
|
|
|
@ -65,6 +65,15 @@ pub fn mul(a: SizeHint, b: SizeHint) -> SizeHint {
|
|||
(low, hi)
|
||||
}
|
||||
|
||||
/// Multiply **x** correctly with a **SizeHint**.
|
||||
#[inline]
|
||||
pub fn mul_scalar(sh: SizeHint, x: usize) -> SizeHint {
|
||||
let (mut low, mut hi) = sh;
|
||||
low = low.saturating_mul(x);
|
||||
hi = hi.and_then(|elt| elt.checked_mul(x));
|
||||
(low, hi)
|
||||
}
|
||||
|
||||
/// Return the maximum
|
||||
#[inline]
|
||||
pub fn max(a: SizeHint, b: SizeHint) -> SizeHint {
|
||||
|
|
|
@ -36,7 +36,9 @@ impl<F> fmt::Debug for RepeatCall<F>
|
|||
/// vec![1, 1, 1, 1, 1]
|
||||
/// );
|
||||
/// ```
|
||||
pub fn repeat_call<F>(function: F) -> RepeatCall<F> {
|
||||
pub fn repeat_call<F, A>(function: F) -> RepeatCall<F>
|
||||
where F: FnMut() -> A
|
||||
{
|
||||
RepeatCall { f: function }
|
||||
}
|
||||
|
||||
|
@ -70,19 +72,18 @@ impl<A, F> Iterator for RepeatCall<F>
|
|||
///
|
||||
/// use itertools::unfold;
|
||||
///
|
||||
/// let mut fibonacci = unfold((1_u32, 1_u32), |state| {
|
||||
/// let (ref mut x1, ref mut x2) = *state;
|
||||
///
|
||||
/// let (mut x1, mut x2) = (1u32, 1u32);
|
||||
/// let mut fibonacci = unfold((), move |_| {
|
||||
/// // Attempt to get the next Fibonacci number
|
||||
/// let next = x1.saturating_add(*x2);
|
||||
/// let next = x1.saturating_add(x2);
|
||||
///
|
||||
/// // Shift left: ret <- x1 <- x2 <- next
|
||||
/// let ret = *x1;
|
||||
/// *x1 = *x2;
|
||||
/// *x2 = next;
|
||||
/// let ret = x1;
|
||||
/// x1 = x2;
|
||||
/// x2 = next;
|
||||
///
|
||||
/// // If addition has saturated at the maximum, we are finished
|
||||
/// if ret == *x1 && ret > 1 {
|
||||
/// if ret == x1 && ret > 1 {
|
||||
/// return None;
|
||||
/// }
|
||||
///
|
||||
|
@ -110,6 +111,7 @@ impl<St, F> fmt::Debug for Unfold<St, F>
|
|||
|
||||
/// See [`unfold`](../fn.unfold.html) for more information.
|
||||
#[derive(Clone)]
|
||||
#[must_use = "iterators are lazy and do nothing unless consumed"]
|
||||
pub struct Unfold<St, F> {
|
||||
f: F,
|
||||
/// Internal state that will be passed to the closure on the next iteration
|
||||
|
@ -139,6 +141,7 @@ impl<A, St, F> Iterator for Unfold<St, F>
|
|||
///
|
||||
/// [`iterate()`]: ../fn.iterate.html
|
||||
#[derive(Clone)]
|
||||
#[must_use = "iterators are lazy and do nothing unless consumed"]
|
||||
pub struct Iterate<St, F> {
|
||||
state: St,
|
||||
f: F,
|
||||
|
|
|
@ -15,6 +15,7 @@ struct TeeBuffer<A, I> {
|
|||
/// One half of an iterator pair where both return the same elements.
|
||||
///
|
||||
/// See [`.tee()`](../trait.Itertools.html#method.tee) for more information.
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct Tee<I>
|
||||
where I: Iterator
|
||||
{
|
||||
|
|
|
@ -60,6 +60,7 @@ impl<T> ExactSizeIterator for TupleBuffer<T>
|
|||
/// An iterator that groups the items in tuples of a specific size.
|
||||
///
|
||||
/// See [`.tuples()`](../trait.Itertools.html#method.tuples) for more information.
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct Tuples<I, T>
|
||||
where I: Iterator<Item = T::Item>,
|
||||
T: TupleCollect
|
||||
|
@ -114,6 +115,7 @@ impl<I, T> Tuples<I, T>
|
|||
///
|
||||
/// See [`.tuple_windows()`](../trait.Itertools.html#method.tuple_windows) for more
|
||||
/// information.
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct TupleWindows<I, T>
|
||||
where I: Iterator<Item = T::Item>,
|
||||
T: TupleCollect
|
||||
|
@ -189,7 +191,7 @@ macro_rules! impl_tuple_collect {
|
|||
type Item = $A;
|
||||
type Buffer = [Option<$A>; $N - 1];
|
||||
|
||||
#[allow(unused_assignments)]
|
||||
#[allow(unused_assignments, unused_mut)]
|
||||
fn collect_from_iter<I>(iter: I, buf: &mut Self::Buffer) -> Option<Self>
|
||||
where I: IntoIterator<Item = $A>
|
||||
{
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
|
||||
use std::collections::HashMap;
|
||||
use std::collections::hash_map::{Entry};
|
||||
use std::hash::Hash;
|
||||
use std::fmt;
|
||||
|
||||
/// An iterator adapter to filter out duplicate elements.
|
||||
///
|
||||
/// See [`.unique_by()`](../trait.Itertools.html#method.unique) for more information.
|
||||
#[derive(Clone)]
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct UniqueBy<I: Iterator, V, F> {
|
||||
iter: I,
|
||||
// Use a hashmap for the entry API
|
||||
used: HashMap<V, ()>,
|
||||
f: F,
|
||||
}
|
||||
|
||||
impl<I, V, F> fmt::Debug for UniqueBy<I, V, F>
|
||||
where I: Iterator + fmt::Debug,
|
||||
V: fmt::Debug + Hash + Eq,
|
||||
{
|
||||
debug_fmt_fields!(UniqueBy, iter, used);
|
||||
}
|
||||
|
||||
/// Create a new `UniqueBy` iterator.
|
||||
pub fn unique_by<I, V, F>(iter: I, f: F) -> UniqueBy<I, V, F>
|
||||
where V: Eq + Hash,
|
||||
F: FnMut(&I::Item) -> V,
|
||||
I: Iterator,
|
||||
{
|
||||
UniqueBy {
|
||||
iter: iter,
|
||||
used: HashMap::new(),
|
||||
f: f,
|
||||
}
|
||||
}
|
||||
|
||||
// count the number of new unique keys in iterable (`used` is the set already seen)
|
||||
fn count_new_keys<I, K>(mut used: HashMap<K, ()>, iterable: I) -> usize
|
||||
where I: IntoIterator<Item=K>,
|
||||
K: Hash + Eq,
|
||||
{
|
||||
let iter = iterable.into_iter();
|
||||
let current_used = used.len();
|
||||
used.extend(iter.map(|key| (key, ())));
|
||||
used.len() - current_used
|
||||
}
|
||||
|
||||
impl<I, V, F> Iterator for UniqueBy<I, V, F>
|
||||
where I: Iterator,
|
||||
V: Eq + Hash,
|
||||
F: FnMut(&I::Item) -> V
|
||||
{
|
||||
type Item = I::Item;
|
||||
|
||||
fn next(&mut self) -> Option<I::Item> {
|
||||
loop {
|
||||
match self.iter.next() {
|
||||
None => return None,
|
||||
Some(v) => {
|
||||
let key = (self.f)(&v);
|
||||
if self.used.insert(key, ()).is_none() {
|
||||
return Some(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let (low, hi) = self.iter.size_hint();
|
||||
((low > 0 && self.used.is_empty()) as usize, hi)
|
||||
}
|
||||
|
||||
fn count(self) -> usize {
|
||||
let mut key_f = self.f;
|
||||
count_new_keys(self.used, self.iter.map(move |elt| key_f(&elt)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> Iterator for Unique<I>
|
||||
where I: Iterator,
|
||||
I::Item: Eq + Hash + Clone
|
||||
{
|
||||
type Item = I::Item;
|
||||
|
||||
fn next(&mut self) -> Option<I::Item> {
|
||||
loop {
|
||||
match self.iter.iter.next() {
|
||||
None => return None,
|
||||
Some(v) => {
|
||||
match self.iter.used.entry(v) {
|
||||
Entry::Occupied(_) => { }
|
||||
Entry::Vacant(entry) => {
|
||||
let elt = entry.key().clone();
|
||||
entry.insert(());
|
||||
return Some(elt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let (low, hi) = self.iter.iter.size_hint();
|
||||
((low > 0 && self.iter.used.is_empty()) as usize, hi)
|
||||
}
|
||||
|
||||
fn count(self) -> usize {
|
||||
count_new_keys(self.iter.used, self.iter.iter)
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator adapter to filter out duplicate elements.
|
||||
///
|
||||
/// See [`.unique()`](../trait.Itertools.html#method.unique) for more information.
|
||||
#[derive(Clone)]
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct Unique<I: Iterator> {
|
||||
iter: UniqueBy<I, I::Item, ()>,
|
||||
}
|
||||
|
||||
impl<I> fmt::Debug for Unique<I>
|
||||
where I: Iterator + fmt::Debug,
|
||||
I::Item: Hash + Eq + fmt::Debug,
|
||||
{
|
||||
debug_fmt_fields!(Unique, iter);
|
||||
}
|
||||
|
||||
pub fn unique<I>(iter: I) -> Unique<I>
|
||||
where I: Iterator,
|
||||
I::Item: Eq + Hash,
|
||||
{
|
||||
Unique {
|
||||
iter: UniqueBy {
|
||||
iter: iter,
|
||||
used: HashMap::new(),
|
||||
f: (),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ use std::iter::{Fuse,Peekable};
|
|||
/// Iterator element type is `Position<I::Item>`.
|
||||
///
|
||||
/// See [`.with_position()`](../trait.Itertools.html#method.with_position) for more information.
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct WithPosition<I>
|
||||
where I: Iterator,
|
||||
{
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use std::cmp::Ordering::{Equal, Greater, Less};
|
||||
use super::size_hint;
|
||||
use std::iter::Fuse;
|
||||
use self::EitherOrBoth::{Right, Left, Both};
|
||||
|
||||
use either_or_both::EitherOrBoth;
|
||||
|
||||
// ZipLongest originally written by SimonSapin,
|
||||
// and dedicated to itertools https://github.com/rust-lang/rust/pull/19283
|
||||
|
@ -39,9 +40,9 @@ impl<T, U> Iterator for ZipLongest<T, U>
|
|||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match (self.a.next(), self.b.next()) {
|
||||
(None, None) => None,
|
||||
(Some(a), None) => Some(Left(a)),
|
||||
(None, Some(b)) => Some(Right(b)),
|
||||
(Some(a), Some(b)) => Some(Both(a, b)),
|
||||
(Some(a), None) => Some(EitherOrBoth::Left(a)),
|
||||
(None, Some(b)) => Some(EitherOrBoth::Right(b)),
|
||||
(Some(a), Some(b)) => Some(EitherOrBoth::Both(a, b)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,13 +61,13 @@ impl<T, U> DoubleEndedIterator for ZipLongest<T, U>
|
|||
match self.a.len().cmp(&self.b.len()) {
|
||||
Equal => match (self.a.next_back(), self.b.next_back()) {
|
||||
(None, None) => None,
|
||||
(Some(a), Some(b)) => Some(Both(a, b)),
|
||||
(Some(a), Some(b)) => Some(EitherOrBoth::Both(a, b)),
|
||||
// These can only happen if .len() is inconsistent with .next_back()
|
||||
(Some(a), None) => Some(Left(a)),
|
||||
(None, Some(b)) => Some(Right(b)),
|
||||
(Some(a), None) => Some(EitherOrBoth::Left(a)),
|
||||
(None, Some(b)) => Some(EitherOrBoth::Right(b)),
|
||||
},
|
||||
Greater => self.a.next_back().map(Left),
|
||||
Less => self.b.next_back().map(Right),
|
||||
Greater => self.a.next_back().map(EitherOrBoth::Left),
|
||||
Less => self.b.next_back().map(EitherOrBoth::Right),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,20 +76,3 @@ impl<T, U> ExactSizeIterator for ZipLongest<T, U>
|
|||
where T: ExactSizeIterator,
|
||||
U: ExactSizeIterator
|
||||
{}
|
||||
|
||||
|
||||
/// A value yielded by `ZipLongest`.
|
||||
/// Contains one or two values, depending on which of the input iterators are exhausted.
|
||||
///
|
||||
/// See [`.zip_longest()`](trait.Itertools.html#method.zip_longest) for more information.
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub enum EitherOrBoth<A, B> {
|
||||
/// Neither input iterator is exhausted yet, yielding two values.
|
||||
Both(A, B),
|
||||
/// The parameter iterator of `.zip_longest()` is exhausted,
|
||||
/// only yielding a value from the `self` iterator.
|
||||
Left(A),
|
||||
/// The `self` iterator of `.zip_longest()` is exhausted,
|
||||
/// only yielding a value from the parameter iterator.
|
||||
Right(B),
|
||||
}
|
||||
|
|
|
@ -2,21 +2,11 @@ use super::size_hint;
|
|||
|
||||
/// See [`multizip`](../fn.multizip.html) for more information.
|
||||
#[derive(Clone)]
|
||||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
||||
pub struct Zip<T> {
|
||||
t: T,
|
||||
}
|
||||
|
||||
impl<T> Zip<T> {
|
||||
/// Deprecated: renamed to multizip
|
||||
#[deprecated(note = "Renamed to multizip")]
|
||||
pub fn new<U>(t: U) -> Zip<T>
|
||||
where Zip<T>: From<U>,
|
||||
Zip<T>: Iterator,
|
||||
{
|
||||
multizip(t)
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator that generalizes *.zip()* and allows running multiple iterators in lockstep.
|
||||
///
|
||||
/// The iterator `Zip<(I, J, ..., M)>` is formed from a tuple of iterators (or values that
|
||||
|
@ -26,18 +16,27 @@ impl<T> Zip<T> {
|
|||
/// The iterator element type is a tuple like like `(A, B, ..., E)` where `A` to `E` are the
|
||||
/// element types of the subiterator.
|
||||
///
|
||||
/// **Note:** The result of this macro is a value of a named type (`Zip<(I, J,
|
||||
/// ..)>` of each component iterator `I, J, ...`) if each component iterator is
|
||||
/// nameable.
|
||||
///
|
||||
/// Prefer [`izip!()`] over `multizip` for the performance benefits of using the
|
||||
/// standard library `.zip()`. Prefer `multizip` if a nameable type is needed.
|
||||
///
|
||||
/// [`izip!()`]: macro.izip.html
|
||||
///
|
||||
/// ```
|
||||
/// use itertools::multizip;
|
||||
///
|
||||
/// // Iterate over three sequences side-by-side
|
||||
/// let mut xs = [0, 0, 0];
|
||||
/// let ys = [69, 107, 101];
|
||||
/// // iterate over three sequences side-by-side
|
||||
/// let mut results = [0, 0, 0, 0];
|
||||
/// let inputs = [3, 7, 9, 6];
|
||||
///
|
||||
/// for (i, a, b) in multizip((0..100, &mut xs, &ys)) {
|
||||
/// *a = i ^ *b;
|
||||
/// for (r, index, input) in multizip((&mut results, 0..10, &inputs)) {
|
||||
/// *r = index * 10 + input;
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(xs, [69, 106, 103]);
|
||||
/// assert_eq!(results, [0 + 3, 10 + 7, 29, 36]);
|
||||
/// ```
|
||||
pub fn multizip<T, U>(t: U) -> Zip<T>
|
||||
where Zip<T>: From<U>,
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
extern crate itertools;
|
||||
|
||||
use itertools::EitherOrBoth;
|
||||
use itertools::free::merge_join_by;
|
||||
|
||||
#[test]
|
||||
fn empty() {
|
||||
let left: Vec<u32> = vec![];
|
||||
let right: Vec<u32> = vec![];
|
||||
let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![];
|
||||
let actual_result = merge_join_by(left, right, |l, r| l.cmp(r))
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(expected_result, actual_result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn left_only() {
|
||||
let left: Vec<u32> = vec![1,2,3];
|
||||
let right: Vec<u32> = vec![];
|
||||
let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![
|
||||
EitherOrBoth::Left(1),
|
||||
EitherOrBoth::Left(2),
|
||||
EitherOrBoth::Left(3)
|
||||
];
|
||||
let actual_result = merge_join_by(left, right, |l, r| l.cmp(r))
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(expected_result, actual_result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn right_only() {
|
||||
let left: Vec<u32> = vec![];
|
||||
let right: Vec<u32> = vec![1,2,3];
|
||||
let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![
|
||||
EitherOrBoth::Right(1),
|
||||
EitherOrBoth::Right(2),
|
||||
EitherOrBoth::Right(3)
|
||||
];
|
||||
let actual_result = merge_join_by(left, right, |l, r| l.cmp(r))
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(expected_result, actual_result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn first_left_then_right() {
|
||||
let left: Vec<u32> = vec![1,2,3];
|
||||
let right: Vec<u32> = vec![4,5,6];
|
||||
let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![
|
||||
EitherOrBoth::Left(1),
|
||||
EitherOrBoth::Left(2),
|
||||
EitherOrBoth::Left(3),
|
||||
EitherOrBoth::Right(4),
|
||||
EitherOrBoth::Right(5),
|
||||
EitherOrBoth::Right(6)
|
||||
];
|
||||
let actual_result = merge_join_by(left, right, |l, r| l.cmp(r))
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(expected_result, actual_result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn first_right_then_left() {
|
||||
let left: Vec<u32> = vec![4,5,6];
|
||||
let right: Vec<u32> = vec![1,2,3];
|
||||
let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![
|
||||
EitherOrBoth::Right(1),
|
||||
EitherOrBoth::Right(2),
|
||||
EitherOrBoth::Right(3),
|
||||
EitherOrBoth::Left(4),
|
||||
EitherOrBoth::Left(5),
|
||||
EitherOrBoth::Left(6)
|
||||
];
|
||||
let actual_result = merge_join_by(left, right, |l, r| l.cmp(r))
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(expected_result, actual_result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interspersed_left_and_right() {
|
||||
let left: Vec<u32> = vec![1,3,5];
|
||||
let right: Vec<u32> = vec![2,4,6];
|
||||
let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![
|
||||
EitherOrBoth::Left(1),
|
||||
EitherOrBoth::Right(2),
|
||||
EitherOrBoth::Left(3),
|
||||
EitherOrBoth::Right(4),
|
||||
EitherOrBoth::Left(5),
|
||||
EitherOrBoth::Right(6)
|
||||
];
|
||||
let actual_result = merge_join_by(left, right, |l, r| l.cmp(r))
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(expected_result, actual_result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overlapping_left_and_right() {
|
||||
let left: Vec<u32> = vec![1,3,4,6];
|
||||
let right: Vec<u32> = vec![2,3,4,5];
|
||||
let expected_result: Vec<EitherOrBoth<u32, u32>> = vec![
|
||||
EitherOrBoth::Left(1),
|
||||
EitherOrBoth::Right(2),
|
||||
EitherOrBoth::Both(3, 3),
|
||||
EitherOrBoth::Both(4, 4),
|
||||
EitherOrBoth::Right(5),
|
||||
EitherOrBoth::Left(6)
|
||||
];
|
||||
let actual_result = merge_join_by(left, right, |l, r| l.cmp(r))
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(expected_result, actual_result);
|
||||
}
|
|
@ -30,34 +30,115 @@ use itertools::free::{
|
|||
|
||||
use quickcheck::TestResult;
|
||||
|
||||
/// Trait for size hint modifier types
|
||||
trait HintKind: Copy + Send + qc::Arbitrary {
|
||||
fn loosen_bounds(&self, org_hint: (usize, Option<usize>)) -> (usize, Option<usize>);
|
||||
}
|
||||
|
||||
/// Exact size hint variant that leaves hints unchanged
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct Exact {}
|
||||
|
||||
impl HintKind for Exact {
|
||||
fn loosen_bounds(&self, org_hint: (usize, Option<usize>)) -> (usize, Option<usize>) {
|
||||
org_hint
|
||||
}
|
||||
}
|
||||
|
||||
impl qc::Arbitrary for Exact {
|
||||
fn arbitrary<G: qc::Gen>(_: &mut G) -> Self {
|
||||
Exact {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Inexact size hint variant to simulate imprecise (but valid) size hints
|
||||
///
|
||||
/// Will always decrease the lower bound and increase the upper bound
|
||||
/// of the size hint by set amounts.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct Inexact {
|
||||
underestimate: usize,
|
||||
overestimate: usize,
|
||||
}
|
||||
|
||||
impl HintKind for Inexact {
|
||||
fn loosen_bounds(&self, org_hint: (usize, Option<usize>)) -> (usize, Option<usize>) {
|
||||
let (org_lower, org_upper) = org_hint;
|
||||
(org_lower.saturating_sub(self.underestimate),
|
||||
org_upper.and_then(move |x| x.checked_add(self.overestimate)))
|
||||
}
|
||||
}
|
||||
|
||||
impl qc::Arbitrary for Inexact {
|
||||
fn arbitrary<G: qc::Gen>(g: &mut G) -> Self {
|
||||
let ue_value = usize::arbitrary(g);
|
||||
let oe_value = usize::arbitrary(g);
|
||||
// Compensate for quickcheck using extreme values too rarely
|
||||
let ue_choices = &[0, ue_value, usize::max_value()];
|
||||
let oe_choices = &[0, oe_value, usize::max_value()];
|
||||
Inexact {
|
||||
underestimate: *g.choose(ue_choices).unwrap(),
|
||||
overestimate: *g.choose(oe_choices).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
fn shrink(&self) -> Box<Iterator<Item=Self>> {
|
||||
let underestimate_value = self.underestimate;
|
||||
let overestimate_value = self.overestimate;
|
||||
Box::new(
|
||||
underestimate_value.shrink().flat_map(move |ue_value|
|
||||
overestimate_value.shrink().map(move |oe_value|
|
||||
Inexact {
|
||||
underestimate: ue_value,
|
||||
overestimate: oe_value,
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Our base iterator that we can impl Arbitrary for
|
||||
///
|
||||
/// By default we'll return inexact bounds estimates for size_hint
|
||||
/// to make tests harder to pass.
|
||||
///
|
||||
/// NOTE: Iter is tricky and is not fused, to help catch bugs.
|
||||
/// At the end it will return None once, then return Some(0),
|
||||
/// then return None again.
|
||||
#[derive(Clone, Debug)]
|
||||
struct Iter<T>(Range<T>, i32); // with fuse/done flag
|
||||
struct Iter<T, SK: HintKind = Inexact> {
|
||||
iterator: Range<T>,
|
||||
// fuse/done flag
|
||||
fuse_flag: i32,
|
||||
hint_kind: SK,
|
||||
}
|
||||
|
||||
impl<T> Iter<T>
|
||||
impl<T, HK> Iter<T, HK> where HK: HintKind
|
||||
{
|
||||
fn new(it: Range<T>) -> Self
|
||||
{
|
||||
Iter(it, 0)
|
||||
fn new(it: Range<T>, hint_kind: HK) -> Self {
|
||||
Iter {
|
||||
iterator: it,
|
||||
fuse_flag: 0,
|
||||
hint_kind: hint_kind
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Iterator for Iter<T> where Range<T>: Iterator,
|
||||
impl<T, HK> Iterator for Iter<T, HK>
|
||||
where Range<T>: Iterator,
|
||||
<Range<T> as Iterator>::Item: Default,
|
||||
HK: HintKind,
|
||||
{
|
||||
type Item = <Range<T> as Iterator>::Item;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item>
|
||||
{
|
||||
let elt = self.0.next();
|
||||
let elt = self.iterator.next();
|
||||
if elt.is_none() {
|
||||
self.1 += 1;
|
||||
self.fuse_flag += 1;
|
||||
// check fuse flag
|
||||
if self.1 == 2 {
|
||||
if self.fuse_flag == 2 {
|
||||
return Some(Default::default())
|
||||
}
|
||||
}
|
||||
|
@ -66,36 +147,101 @@ impl<T> Iterator for Iter<T> where Range<T>: Iterator,
|
|||
|
||||
fn size_hint(&self) -> (usize, Option<usize>)
|
||||
{
|
||||
self.0.size_hint()
|
||||
let org_hint = self.iterator.size_hint();
|
||||
self.hint_kind.loosen_bounds(org_hint)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DoubleEndedIterator for Iter<T> where Range<T>: DoubleEndedIterator,
|
||||
impl<T, HK> DoubleEndedIterator for Iter<T, HK>
|
||||
where Range<T>: DoubleEndedIterator,
|
||||
<Range<T> as Iterator>::Item: Default,
|
||||
HK: HintKind
|
||||
{
|
||||
fn next_back(&mut self) -> Option<Self::Item> { self.0.next_back() }
|
||||
fn next_back(&mut self) -> Option<Self::Item> { self.iterator.next_back() }
|
||||
}
|
||||
|
||||
impl<T> ExactSizeIterator for Iter<T> where Range<T>: ExactSizeIterator,
|
||||
impl<T> ExactSizeIterator for Iter<T, Exact> where Range<T>: ExactSizeIterator,
|
||||
<Range<T> as Iterator>::Item: Default,
|
||||
{ }
|
||||
|
||||
impl<T> qc::Arbitrary for Iter<T> where T: qc::Arbitrary
|
||||
impl<T, HK> qc::Arbitrary for Iter<T, HK>
|
||||
where T: qc::Arbitrary,
|
||||
HK: HintKind,
|
||||
{
|
||||
fn arbitrary<G: qc::Gen>(g: &mut G) -> Self
|
||||
{
|
||||
Iter::new(T::arbitrary(g)..T::arbitrary(g))
|
||||
Iter::new(T::arbitrary(g)..T::arbitrary(g), HK::arbitrary(g))
|
||||
}
|
||||
|
||||
fn shrink(&self) -> Box<Iterator<Item=Iter<T>>>
|
||||
fn shrink(&self) -> Box<Iterator<Item=Iter<T, HK>>>
|
||||
{
|
||||
let r = self.0.clone();
|
||||
let r = self.iterator.clone();
|
||||
let hint_kind = self.hint_kind;
|
||||
Box::new(
|
||||
r.start.shrink().flat_map(move |x| {
|
||||
r.end.shrink().map(move |y| (x.clone(), y))
|
||||
})
|
||||
.map(|(a, b)| Iter::new(a..b))
|
||||
r.start.shrink().flat_map(move |a|
|
||||
r.end.shrink().map(move |b|
|
||||
Iter::new(a.clone()..b, hint_kind)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// A meta-iterator which yields `Iter<i32>`s whose start/endpoints are
|
||||
/// increased or decreased linearly on each iteration.
|
||||
#[derive(Clone, Debug)]
|
||||
struct ShiftRange<HK = Inexact> {
|
||||
range_start: i32,
|
||||
range_end: i32,
|
||||
start_step: i32,
|
||||
end_step: i32,
|
||||
iter_count: u32,
|
||||
hint_kind: HK,
|
||||
}
|
||||
|
||||
impl<HK> Iterator for ShiftRange<HK> where HK: HintKind {
|
||||
type Item = Iter<i32, HK>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.iter_count == 0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let iter = Iter::new(self.range_start..self.range_end, self.hint_kind);
|
||||
|
||||
self.range_start += self.start_step;
|
||||
self.range_end += self.end_step;
|
||||
self.iter_count -= 1;
|
||||
|
||||
Some(iter)
|
||||
}
|
||||
}
|
||||
|
||||
impl ExactSizeIterator for ShiftRange<Exact> { }
|
||||
|
||||
impl<HK> qc::Arbitrary for ShiftRange<HK>
|
||||
where HK: HintKind
|
||||
{
|
||||
fn arbitrary<G: qc::Gen>(g: &mut G) -> Self {
|
||||
const MAX_STARTING_RANGE_DIFF: i32 = 32;
|
||||
const MAX_STEP_MODULO: i32 = 8;
|
||||
const MAX_ITER_COUNT: u32 = 3;
|
||||
|
||||
let range_start = qc::Arbitrary::arbitrary(g);
|
||||
let range_end = range_start + g.gen_range(0, MAX_STARTING_RANGE_DIFF + 1);
|
||||
let start_step = g.gen_range(-MAX_STEP_MODULO, MAX_STEP_MODULO + 1);
|
||||
let end_step = g.gen_range(-MAX_STEP_MODULO, MAX_STEP_MODULO + 1);
|
||||
let iter_count = g.gen_range(0, MAX_ITER_COUNT + 1);
|
||||
let hint_kind = qc::Arbitrary::arbitrary(g);
|
||||
|
||||
ShiftRange {
|
||||
range_start: range_start,
|
||||
range_end: range_end,
|
||||
start_step: start_step,
|
||||
end_step: end_step,
|
||||
iter_count: iter_count,
|
||||
hint_kind: hint_kind
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -179,7 +325,6 @@ macro_rules! quickcheck {
|
|||
// The property functions can use pattern matching and `mut` as usual
|
||||
// in the function arguments, but the functions can not be generic.
|
||||
{$($(#$attr:tt)* fn $fn_name:ident($($arg:tt)*) -> $ret:ty { $($code:tt)* })*} => (
|
||||
quickcheck!{@as_items
|
||||
$(
|
||||
#[test]
|
||||
$(#$attr)*
|
||||
|
@ -190,11 +335,10 @@ macro_rules! quickcheck {
|
|||
::quickcheck::quickcheck(quickcheck!(@fn prop [] $($arg)*));
|
||||
}
|
||||
)*
|
||||
}
|
||||
);
|
||||
// parse argument list (with patterns allowed) into prop as fn(_, _) -> _
|
||||
(@fn $f:ident [$($t:tt)*]) => {
|
||||
quickcheck!(@as_expr $f as fn($($t),*) -> _)
|
||||
$f as fn($($t),*) -> _
|
||||
};
|
||||
(@fn $f:ident [$($p:tt)*] : $($tail:tt)*) => {
|
||||
quickcheck!(@fn $f [$($p)* _] $($tail)*)
|
||||
|
@ -202,8 +346,6 @@ macro_rules! quickcheck {
|
|||
(@fn $f:ident [$($p:tt)*] $t:tt $($tail:tt)*) => {
|
||||
quickcheck!(@fn $f [$($p)*] $($tail)*)
|
||||
};
|
||||
(@as_items $($i:item)*) => ($($i)*);
|
||||
(@as_expr $i:expr) => ($i);
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
|
@ -215,7 +357,52 @@ quickcheck! {
|
|||
correct_size_hint(iproduct!(a, b, c))
|
||||
}
|
||||
|
||||
fn size_step(a: Iter<i16>, s: usize) -> bool {
|
||||
fn correct_cartesian_product3(a: Iter<u16>, b: Iter<u16>, c: Iter<u16>,
|
||||
take_manual: usize) -> ()
|
||||
{
|
||||
// test correctness of iproduct through regular iteration (take)
|
||||
// and through fold.
|
||||
let ac = a.clone();
|
||||
let br = &b.clone();
|
||||
let cr = &c.clone();
|
||||
let answer: Vec<_> = ac.flat_map(move |ea| br.clone().flat_map(move |eb| cr.clone().map(move |ec| (ea, eb, ec)))).collect();
|
||||
let mut product_iter = iproduct!(a, b, c);
|
||||
let mut actual = Vec::new();
|
||||
|
||||
actual.extend((&mut product_iter).take(take_manual));
|
||||
if actual.len() == take_manual {
|
||||
product_iter.fold((), |(), elt| actual.push(elt));
|
||||
}
|
||||
assert_eq!(answer, actual);
|
||||
}
|
||||
|
||||
fn size_multi_product(a: ShiftRange) -> bool {
|
||||
correct_size_hint(a.multi_cartesian_product())
|
||||
}
|
||||
fn correct_multi_product3(a: ShiftRange, take_manual: usize) -> () {
|
||||
// Fix no. of iterators at 3
|
||||
let a = ShiftRange { iter_count: 3, ..a };
|
||||
|
||||
// test correctness of MultiProduct through regular iteration (take)
|
||||
// and through fold.
|
||||
let mut iters = a.clone();
|
||||
let i0 = iters.next().unwrap();
|
||||
let i1r = &iters.next().unwrap();
|
||||
let i2r = &iters.next().unwrap();
|
||||
let answer: Vec<_> = i0.flat_map(move |ei0| i1r.clone().flat_map(move |ei1| i2r.clone().map(move |ei2| vec![ei0, ei1, ei2]))).collect();
|
||||
let mut multi_product = a.clone().multi_cartesian_product();
|
||||
let mut actual = Vec::new();
|
||||
|
||||
actual.extend((&mut multi_product).take(take_manual));
|
||||
if actual.len() == take_manual {
|
||||
multi_product.fold((), |(), elt| actual.push(elt));
|
||||
}
|
||||
assert_eq!(answer, actual);
|
||||
|
||||
assert_eq!(answer.into_iter().last(), a.clone().multi_cartesian_product().last());
|
||||
}
|
||||
|
||||
fn size_step(a: Iter<i16, Exact>, s: usize) -> bool {
|
||||
let mut s = s;
|
||||
if s == 0 {
|
||||
s += 1; // never zero
|
||||
|
@ -249,7 +436,7 @@ quickcheck! {
|
|||
}))
|
||||
}
|
||||
|
||||
fn size_multipeek(a: Iter<u16>, s: u8) -> bool {
|
||||
fn size_multipeek(a: Iter<u16, Exact>, s: u8) -> bool {
|
||||
let mut it = multipeek(a);
|
||||
// peek a few times
|
||||
for _ in 0..s {
|
||||
|
@ -271,7 +458,7 @@ quickcheck! {
|
|||
fn size_merge(a: Iter<u16>, b: Iter<u16>) -> bool {
|
||||
correct_size_hint(a.merge(b))
|
||||
}
|
||||
fn size_zip(a: Iter<i16>, b: Iter<i16>, c: Iter<i16>) -> bool {
|
||||
fn size_zip(a: Iter<i16, Exact>, b: Iter<i16, Exact>, c: Iter<i16, Exact>) -> bool {
|
||||
let filt = a.clone().dedup();
|
||||
correct_size_hint(multizip((filt, b.clone(), c.clone()))) &&
|
||||
exact_size(multizip((a, b, c)))
|
||||
|
@ -281,6 +468,11 @@ quickcheck! {
|
|||
correct_size_hint(multizip((&rc, &rc, b)))
|
||||
}
|
||||
|
||||
fn size_zip_macro(a: Iter<i16, Exact>, b: Iter<i16, Exact>, c: Iter<i16, Exact>) -> bool {
|
||||
let filt = a.clone().dedup();
|
||||
correct_size_hint(izip!(filt, b.clone(), c.clone())) &&
|
||||
exact_size(izip!(a, b, c))
|
||||
}
|
||||
fn equal_kmerge(a: Vec<i16>, b: Vec<i16>, c: Vec<i16>) -> bool {
|
||||
use itertools::free::kmerge;
|
||||
let mut sa = a.clone();
|
||||
|
@ -355,7 +547,7 @@ quickcheck! {
|
|||
let b = &b[..len];
|
||||
itertools::equal(zip_eq(a, b), zip(a, b))
|
||||
}
|
||||
fn size_zip_longest(a: Iter<i16>, b: Iter<i16>) -> bool {
|
||||
fn size_zip_longest(a: Iter<i16, Exact>, b: Iter<i16, Exact>) -> bool {
|
||||
let filt = a.clone().dedup();
|
||||
let filt2 = b.clone().dedup();
|
||||
correct_size_hint(filt.zip_longest(b.clone())) &&
|
||||
|
@ -384,7 +576,7 @@ quickcheck! {
|
|||
fn size_interleave(a: Iter<i16>, b: Iter<i16>) -> bool {
|
||||
correct_size_hint(a.interleave(b))
|
||||
}
|
||||
fn exact_interleave(a: Iter<i16>, b: Iter<i16>) -> bool {
|
||||
fn exact_interleave(a: Iter<i16, Exact>, b: Iter<i16, Exact>) -> bool {
|
||||
exact_size_for_this(a.interleave(b))
|
||||
}
|
||||
fn size_interleave_shortest(a: Iter<i16>, b: Iter<i16>) -> bool {
|
||||
|
@ -420,19 +612,20 @@ quickcheck! {
|
|||
itertools::equal(a.iter().flatten(),
|
||||
a.iter().flat_map(|x| x))
|
||||
}
|
||||
fn equal_flatten_vec_rev(a: Vec<Vec<u8>>) -> bool {
|
||||
itertools::equal(a.iter().flatten().rev(),
|
||||
a.iter().flat_map(|x| x).rev())
|
||||
}
|
||||
|
||||
fn equal_combinations_2(a: Vec<u8>) -> bool {
|
||||
let mut v = Vec::new();
|
||||
for (i, &x) in enumerate(&a) {
|
||||
for &y in &a[i + 1..] {
|
||||
for (i, x) in enumerate(&a) {
|
||||
for y in &a[i + 1..] {
|
||||
v.push((x, y));
|
||||
}
|
||||
}
|
||||
itertools::equal(cloned(&a).tuple_combinations::<(_, _)>(), cloned(&v))
|
||||
itertools::equal(a.iter().tuple_combinations::<(_, _)>(), v)
|
||||
}
|
||||
|
||||
fn collect_tuple_matches_size(a: Iter<i16>) -> bool {
|
||||
let size = a.clone().count();
|
||||
a.collect_tuple::<(_, _, _)>().is_some() == (size == 3)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -548,7 +741,7 @@ quickcheck! {
|
|||
}
|
||||
|
||||
quickcheck! {
|
||||
fn size_pad_tail2(it: Iter<i8>, pad: u8) -> bool {
|
||||
fn size_pad_tail2(it: Iter<i8, Exact>, pad: u8) -> bool {
|
||||
exact_size(it.pad_using(pad as usize, |_| 0))
|
||||
}
|
||||
}
|
||||
|
@ -557,6 +750,18 @@ quickcheck! {
|
|||
fn size_unique(it: Iter<i8>) -> bool {
|
||||
correct_size_hint(it.unique())
|
||||
}
|
||||
|
||||
fn count_unique(it: Vec<i8>, take_first: u8) -> () {
|
||||
let answer = {
|
||||
let mut v = it.clone();
|
||||
v.sort(); v.dedup();
|
||||
v.len()
|
||||
};
|
||||
let mut iter = cloned(&it).unique();
|
||||
let first_count = (&mut iter).take(take_first as usize).count();
|
||||
let rest_count = iter.count();
|
||||
assert_eq!(answer, first_count + rest_count);
|
||||
}
|
||||
}
|
||||
|
||||
quickcheck! {
|
||||
|
@ -698,7 +903,7 @@ quickcheck! {
|
|||
fn with_position_exact_size_1(a: Vec<u8>) -> bool {
|
||||
exact_size_for_this(a.iter().with_position())
|
||||
}
|
||||
fn with_position_exact_size_2(a: Iter<u8>) -> bool {
|
||||
fn with_position_exact_size_2(a: Iter<u8, Exact>) -> bool {
|
||||
exact_size_for_this(a.with_position())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,234 @@
|
|||
//! Licensed under the Apache License, Version 2.0
|
||||
//! http://www.apache.org/licenses/LICENSE-2.0 or the MIT license
|
||||
//! http://opensource.org/licenses/MIT, at your
|
||||
//! option. This file may not be copied, modified, or distributed
|
||||
//! except according to those terms.
|
||||
#![no_std]
|
||||
|
||||
#[macro_use] extern crate itertools as it;
|
||||
|
||||
use it::Itertools;
|
||||
use it::interleave;
|
||||
use it::multizip;
|
||||
use it::free::put_back;
|
||||
|
||||
#[test]
|
||||
fn product2() {
|
||||
let s = "αβ";
|
||||
|
||||
let mut prod = iproduct!(s.chars(), 0..2);
|
||||
assert!(prod.next() == Some(('α', 0)));
|
||||
assert!(prod.next() == Some(('α', 1)));
|
||||
assert!(prod.next() == Some(('β', 0)));
|
||||
assert!(prod.next() == Some(('β', 1)));
|
||||
assert!(prod.next() == None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn product_temporary() {
|
||||
for (_x, _y, _z) in iproduct!(
|
||||
[0, 1, 2].iter().cloned(),
|
||||
[0, 1, 2].iter().cloned(),
|
||||
[0, 1, 2].iter().cloned())
|
||||
{
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn izip_macro() {
|
||||
let mut zip = izip!(2..3);
|
||||
assert!(zip.next() == Some(2));
|
||||
assert!(zip.next().is_none());
|
||||
|
||||
let mut zip = izip!(0..3, 0..2, 0..2i8);
|
||||
for i in 0..2 {
|
||||
assert!((i as usize, i, i as i8) == zip.next().unwrap());
|
||||
}
|
||||
assert!(zip.next().is_none());
|
||||
|
||||
let xs: [isize; 0] = [];
|
||||
let mut zip = izip!(0..3, 0..2, 0..2i8, &xs);
|
||||
assert!(zip.next().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn izip3() {
|
||||
let mut zip = multizip((0..3, 0..2, 0..2i8));
|
||||
for i in 0..2 {
|
||||
assert!((i as usize, i, i as i8) == zip.next().unwrap());
|
||||
}
|
||||
assert!(zip.next().is_none());
|
||||
|
||||
let xs: [isize; 0] = [];
|
||||
let mut zip = multizip((0..3, 0..2, 0..2i8, xs.iter()));
|
||||
assert!(zip.next().is_none());
|
||||
|
||||
for (_, _, _, _, _) in multizip((0..3, 0..2, xs.iter(), &xs, xs.to_vec())) {
|
||||
/* test compiles */
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_to() {
|
||||
let xs = [7, 9, 8];
|
||||
let mut ys = [0; 5];
|
||||
let cnt = ys.iter_mut().set_from(xs.iter().map(|x| *x));
|
||||
assert!(cnt == xs.len());
|
||||
assert!(ys == [7, 9, 8, 0, 0]);
|
||||
|
||||
let cnt = ys.iter_mut().set_from(0..10);
|
||||
assert!(cnt == ys.len());
|
||||
assert!(ys == [0, 1, 2, 3, 4]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_interleave() {
|
||||
let xs: [u8; 0] = [];
|
||||
let ys = [7u8, 9, 8, 10];
|
||||
let zs = [2u8, 77];
|
||||
let it = interleave(xs.iter(), ys.iter());
|
||||
it::assert_equal(it, ys.iter());
|
||||
|
||||
let rs = [7u8, 2, 9, 77, 8, 10];
|
||||
let it = interleave(ys.iter(), zs.iter());
|
||||
it::assert_equal(it, rs.iter());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn foreach() {
|
||||
let xs = [1i32, 2, 3];
|
||||
let mut sum = 0;
|
||||
xs.iter().foreach(|elt| sum += *elt);
|
||||
assert!(sum == 6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dropping() {
|
||||
let xs = [1, 2, 3];
|
||||
let mut it = xs.iter().dropping(2);
|
||||
assert_eq!(it.next(), Some(&3));
|
||||
assert!(it.next().is_none());
|
||||
let mut it = xs.iter().dropping(5);
|
||||
assert!(it.next().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batching() {
|
||||
let xs = [0, 1, 2, 1, 3];
|
||||
let ys = [(0, 1), (2, 1)];
|
||||
|
||||
// An iterator that gathers elements up in pairs
|
||||
let pit = xs.iter().cloned().batching(|it| {
|
||||
match it.next() {
|
||||
None => None,
|
||||
Some(x) => match it.next() {
|
||||
None => None,
|
||||
Some(y) => Some((x, y)),
|
||||
}
|
||||
}
|
||||
});
|
||||
it::assert_equal(pit, ys.iter().cloned());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_put_back() {
|
||||
let xs = [0, 1, 1, 1, 2, 1, 3, 3];
|
||||
let mut pb = put_back(xs.iter().cloned());
|
||||
pb.next();
|
||||
pb.put_back(1);
|
||||
pb.put_back(0);
|
||||
it::assert_equal(pb, xs.iter().cloned());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn step() {
|
||||
it::assert_equal((0..10).step(1), (0..10));
|
||||
it::assert_equal((0..10).step(2), (0..10).filter(|x: &i32| *x % 2 == 0));
|
||||
it::assert_equal((0..10).step(10), 0..1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merge() {
|
||||
it::assert_equal((0..10).step(2).merge((1..10).step(2)), (0..10));
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn repeatn() {
|
||||
let s = "α";
|
||||
let mut it = it::repeat_n(s, 3);
|
||||
assert_eq!(it.len(), 3);
|
||||
assert_eq!(it.next(), Some(s));
|
||||
assert_eq!(it.next(), Some(s));
|
||||
assert_eq!(it.next(), Some(s));
|
||||
assert_eq!(it.next(), None);
|
||||
assert_eq!(it.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn count_clones() {
|
||||
// Check that RepeatN only clones N - 1 times.
|
||||
|
||||
use core::cell::Cell;
|
||||
#[derive(PartialEq, Debug)]
|
||||
struct Foo {
|
||||
n: Cell<usize>
|
||||
}
|
||||
|
||||
impl Clone for Foo
|
||||
{
|
||||
fn clone(&self) -> Self
|
||||
{
|
||||
let n = self.n.get();
|
||||
self.n.set(n + 1);
|
||||
Foo { n: Cell::new(n + 1) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for n in 0..10 {
|
||||
let f = Foo{n: Cell::new(0)};
|
||||
let it = it::repeat_n(f, n);
|
||||
// drain it
|
||||
let last = it.last();
|
||||
if n == 0 {
|
||||
assert_eq!(last, None);
|
||||
} else {
|
||||
assert_eq!(last, Some(Foo{n: Cell::new(n - 1)}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part() {
|
||||
let mut data = [7, 1, 1, 9, 1, 1, 3];
|
||||
let i = it::partition(&mut data, |elt| *elt >= 3);
|
||||
assert_eq!(i, 3);
|
||||
assert_eq!(data, [7, 3, 9, 1, 1, 1, 1]);
|
||||
|
||||
let i = it::partition(&mut data, |elt| *elt == 1);
|
||||
assert_eq!(i, 4);
|
||||
assert_eq!(data, [1, 1, 1, 1, 9, 3, 7]);
|
||||
|
||||
let mut data = [1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
let i = it::partition(&mut data, |elt| *elt % 3 == 0);
|
||||
assert_eq!(i, 3);
|
||||
assert_eq!(data, [9, 6, 3, 4, 5, 2, 7, 8, 1]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flatten_clone() {
|
||||
let data = &[
|
||||
&[1,2,3],
|
||||
&[4,5,6]
|
||||
];
|
||||
let flattened1 = data.into_iter().cloned().flatten();
|
||||
let flattened2 = flattened1.clone();
|
||||
|
||||
it::assert_equal(flattened1, &[1,2,3,4,5,6]);
|
||||
it::assert_equal(flattened2, &[1,2,3,4,5,6]);
|
||||
}
|
||||
|
||||
|
|
@ -1,34 +1,15 @@
|
|||
//! Licensed under the Apache License, Version 2.0
|
||||
//! http://www.apache.org/licenses/LICENSE-2.0 or the MIT license
|
||||
//! http://opensource.org/licenses/MIT, at your
|
||||
//! option. This file may not be copied, modified, or distributed
|
||||
//! except according to those terms.
|
||||
|
||||
#[macro_use] extern crate itertools as it;
|
||||
extern crate permutohedron;
|
||||
|
||||
use it::Itertools;
|
||||
use it::interleave;
|
||||
use it::multizip;
|
||||
use it::multipeek;
|
||||
use it::FoldWhile;
|
||||
use it::free::rciter;
|
||||
use it::free::put_back;
|
||||
use it::free::put_back_n;
|
||||
use it::FoldWhile;
|
||||
use it::cloned;
|
||||
|
||||
#[test]
|
||||
fn product2() {
|
||||
let s = "αβ";
|
||||
|
||||
let mut prod = iproduct!(s.chars(), 0..2);
|
||||
assert!(prod.next() == Some(('α', 0)));
|
||||
assert!(prod.next() == Some(('α', 1)));
|
||||
assert!(prod.next() == Some(('β', 0)));
|
||||
assert!(prod.next() == Some(('β', 1)));
|
||||
assert!(prod.next() == None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn product3() {
|
||||
let prod = iproduct!(0..3, 0..2, 0..2);
|
||||
|
@ -46,74 +27,6 @@ fn product3() {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn product_temporary() {
|
||||
for (_x, _y, _z) in iproduct!(
|
||||
[0, 1, 2].iter().cloned(),
|
||||
[0, 1, 2].iter().cloned(),
|
||||
[0, 1, 2].iter().cloned())
|
||||
{
|
||||
// ok
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn izip_macro() {
|
||||
let mut zip = izip!(0..3, 0..2, 0..2i8);
|
||||
for i in 0..2 {
|
||||
assert!((i as usize, i, i as i8) == zip.next().unwrap());
|
||||
}
|
||||
assert!(zip.next().is_none());
|
||||
|
||||
let xs: [isize; 0] = [];
|
||||
let mut zip = izip!(0..3, 0..2, 0..2i8, &xs);
|
||||
assert!(zip.next().is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn izip3() {
|
||||
let mut zip = multizip((0..3, 0..2, 0..2i8));
|
||||
for i in 0..2 {
|
||||
assert!((i as usize, i, i as i8) == zip.next().unwrap());
|
||||
}
|
||||
assert!(zip.next().is_none());
|
||||
|
||||
let xs: [isize; 0] = [];
|
||||
let mut zip = multizip((0..3, 0..2, 0..2i8, xs.iter()));
|
||||
assert!(zip.next().is_none());
|
||||
|
||||
for (_, _, _, _, _) in multizip((0..3, 0..2, xs.iter(), &xs, xs.to_vec())) {
|
||||
/* test compiles */
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_to() {
|
||||
let xs = [7, 9, 8];
|
||||
let mut ys = [0; 5];
|
||||
let cnt = ys.iter_mut().set_from(xs.iter().map(|x| *x));
|
||||
assert!(cnt == xs.len());
|
||||
assert!(ys == [7, 9, 8, 0, 0]);
|
||||
|
||||
let cnt = ys.iter_mut().set_from(0..10);
|
||||
assert!(cnt == ys.len());
|
||||
assert!(ys == [0, 1, 2, 3, 4]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_interleave() {
|
||||
let xs: [u8; 0] = [];
|
||||
let ys = [7u8, 9, 8, 10];
|
||||
let zs = [2u8, 77];
|
||||
let it = interleave(xs.iter(), ys.iter());
|
||||
it::assert_equal(it, ys.iter());
|
||||
|
||||
let rs = [7u8, 2, 9, 77, 8, 10];
|
||||
let it = interleave(ys.iter(), zs.iter());
|
||||
it::assert_equal(it, rs.iter());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interleave_shortest() {
|
||||
let v0: Vec<i32> = vec![0, 2, 4];
|
||||
|
@ -139,22 +52,22 @@ fn interleave_shortest() {
|
|||
assert_eq!(it.size_hint(), (6, Some(6)));
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn foreach() {
|
||||
let xs = [1i32, 2, 3];
|
||||
let mut sum = 0;
|
||||
xs.iter().foreach(|elt| sum += *elt);
|
||||
assert!(sum == 6);
|
||||
fn unique_by() {
|
||||
let xs = ["aaa", "bbbbb", "aa", "ccc", "bbbb", "aaaaa", "cccc"];
|
||||
let ys = ["aaa", "bbbbb", "ccc"];
|
||||
it::assert_equal(ys.iter(), xs.iter().unique_by(|x| x[..2].to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dropping() {
|
||||
let xs = [1, 2, 3];
|
||||
let mut it = xs.iter().dropping(2);
|
||||
assert_eq!(it.next(), Some(&3));
|
||||
assert!(it.next().is_none());
|
||||
let mut it = xs.iter().dropping(5);
|
||||
assert!(it.next().is_none());
|
||||
fn unique() {
|
||||
let xs = [0, 1, 2, 3, 2, 1, 3];
|
||||
let ys = [0, 1, 2, 3];
|
||||
it::assert_equal(ys.iter(), xs.iter().unique());
|
||||
let xs = [0, 1];
|
||||
let ys = [0, 1];
|
||||
it::assert_equal(ys.iter(), xs.iter().unique());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -186,48 +99,12 @@ fn dedup() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn unique_by() {
|
||||
let xs = ["aaa", "bbbbb", "aa", "ccc", "bbbb", "aaaaa", "cccc"];
|
||||
let ys = ["aaa", "bbbbb", "ccc"];
|
||||
it::assert_equal(ys.iter(), xs.iter().unique_by(|x| x[..2].to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unique() {
|
||||
let xs = [0, 1, 2, 3, 2, 1, 3];
|
||||
let ys = [0, 1, 2, 3];
|
||||
it::assert_equal(ys.iter(), xs.iter().unique());
|
||||
let xs = [0, 1];
|
||||
let ys = [0, 1];
|
||||
it::assert_equal(ys.iter(), xs.iter().unique());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn batching() {
|
||||
let xs = [0, 1, 2, 1, 3];
|
||||
let ys = [(0, 1), (2, 1)];
|
||||
|
||||
// An iterator that gathers elements up in pairs
|
||||
let pit = xs.iter().cloned().batching(|mut it| {
|
||||
match it.next() {
|
||||
None => None,
|
||||
Some(x) => match it.next() {
|
||||
None => None,
|
||||
Some(y) => Some((x, y)),
|
||||
fn all_equal() {
|
||||
assert!(!"AABBCCC".chars().all_equal());
|
||||
assert!("AAAAAAA".chars().all_equal());
|
||||
for (_key, mut sub) in &"AABBCCC".chars().group_by(|&x| x) {
|
||||
assert!(sub.all_equal());
|
||||
}
|
||||
}
|
||||
});
|
||||
it::assert_equal(pit, ys.iter().cloned());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_put_back() {
|
||||
let xs = [0, 1, 1, 1, 2, 1, 3, 3];
|
||||
let mut pb = put_back(xs.iter().cloned());
|
||||
pb.next();
|
||||
pb.put_back(1);
|
||||
pb.put_back(0);
|
||||
it::assert_equal(pb, xs.iter().cloned());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -288,13 +165,6 @@ fn test_rciter() {
|
|||
assert_eq!(z.next(), Some((0, 1)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn step() {
|
||||
it::assert_equal((0..10).step(1), (0..10));
|
||||
it::assert_equal((0..10).step(2), (0..10).filter(|x: &i32| *x % 2 == 0));
|
||||
it::assert_equal((0..10).step(10), 0..1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trait_pointers() {
|
||||
struct ByRef<'r, I: ?Sized>(&'r mut I) where I: 'r;
|
||||
|
@ -314,7 +184,7 @@ fn trait_pointers() {
|
|||
|
||||
{
|
||||
/* make sure foreach works on non-Sized */
|
||||
let mut jt: &mut Iterator<Item=i32> = &mut *it;
|
||||
let jt: &mut Iterator<Item = i32> = &mut *it;
|
||||
assert_eq!(jt.next(), Some(1));
|
||||
|
||||
{
|
||||
|
@ -327,11 +197,6 @@ fn trait_pointers() {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merge() {
|
||||
it::assert_equal((0..10).step(2).merge((1..10).step(2)), (0..10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn merge_by() {
|
||||
let odd : Vec<(u32, &str)> = vec![(1, "hello"), (3, "world"), (5, "!")];
|
||||
|
@ -409,6 +274,15 @@ fn sorted_by() {
|
|||
assert_eq!(v, vec![4, 3, 2, 1, 0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sorted_by_key() {
|
||||
let sc = [3, 4, 1, 2].iter().cloned().sorted_by_key(|&x| x);
|
||||
assert_eq!(sc, vec![1, 2, 3, 4]);
|
||||
|
||||
let v = (0..5).sorted_by_key(|&x| -x);
|
||||
assert_eq!(v, vec![4, 3, 2, 1, 0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multipeek() {
|
||||
let nums = vec![1u8,2,3,4,5];
|
||||
|
@ -428,6 +302,8 @@ fn test_multipeek() {
|
|||
assert_eq!(mp.peek(), None);
|
||||
assert_eq!(mp.next(), Some(3));
|
||||
assert_eq!(mp.next(), Some(4));
|
||||
assert_eq!(mp.peek(), Some(&5));
|
||||
assert_eq!(mp.peek(), None);
|
||||
assert_eq!(mp.next(), Some(5));
|
||||
assert_eq!(mp.next(), None);
|
||||
assert_eq!(mp.peek(), None);
|
||||
|
@ -449,66 +325,29 @@ fn test_multipeek_reset() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn repeatn() {
|
||||
let s = "α";
|
||||
let mut it = it::repeat_n(s, 3);
|
||||
assert_eq!(it.len(), 3);
|
||||
assert_eq!(it.next(), Some(s));
|
||||
assert_eq!(it.next(), Some(s));
|
||||
assert_eq!(it.next(), Some(s));
|
||||
assert_eq!(it.next(), None);
|
||||
assert_eq!(it.next(), None);
|
||||
}
|
||||
fn test_multipeek_peeking_next() {
|
||||
use it::PeekingNext;
|
||||
let nums = vec![1u8,2,3,4,5,6,7];
|
||||
|
||||
#[test]
|
||||
fn count_clones() {
|
||||
// Check that RepeatN only clones N - 1 times.
|
||||
|
||||
use std::cell::Cell;
|
||||
#[derive(PartialEq, Debug)]
|
||||
struct Foo {
|
||||
n: Cell<usize>
|
||||
}
|
||||
|
||||
impl Clone for Foo
|
||||
{
|
||||
fn clone(&self) -> Self
|
||||
{
|
||||
let n = self.n.get();
|
||||
self.n.set(n + 1);
|
||||
Foo { n: Cell::new(n + 1) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for n in 0..10 {
|
||||
let f = Foo{n: Cell::new(0)};
|
||||
let it = it::repeat_n(f, n);
|
||||
// drain it
|
||||
let last = it.last();
|
||||
if n == 0 {
|
||||
assert_eq!(last, None);
|
||||
} else {
|
||||
assert_eq!(last, Some(Foo{n: Cell::new(n - 1)}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part() {
|
||||
let mut data = [7, 1, 1, 9, 1, 1, 3];
|
||||
let i = it::partition(&mut data, |elt| *elt >= 3);
|
||||
assert_eq!(i, 3);
|
||||
assert_eq!(data, [7, 3, 9, 1, 1, 1, 1]);
|
||||
|
||||
let i = it::partition(&mut data, |elt| *elt == 1);
|
||||
assert_eq!(i, 4);
|
||||
assert_eq!(data, [1, 1, 1, 1, 9, 3, 7]);
|
||||
|
||||
let mut data = [1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||
let i = it::partition(&mut data, |elt| *elt % 3 == 0);
|
||||
assert_eq!(i, 3);
|
||||
assert_eq!(data, [9, 6, 3, 4, 5, 2, 7, 8, 1]);
|
||||
let mut mp = multipeek(nums.iter().map(|&x| x));
|
||||
assert_eq!(mp.peeking_next(|&x| x != 0), Some(1));
|
||||
assert_eq!(mp.next(), Some(2));
|
||||
assert_eq!(mp.peek(), Some(&3));
|
||||
assert_eq!(mp.peek(), Some(&4));
|
||||
assert_eq!(mp.peeking_next(|&x| x == 3), Some(3));
|
||||
assert_eq!(mp.peek(), Some(&4));
|
||||
assert_eq!(mp.peeking_next(|&x| x != 4), None);
|
||||
assert_eq!(mp.peeking_next(|&x| x == 4), Some(4));
|
||||
assert_eq!(mp.peek(), Some(&5));
|
||||
assert_eq!(mp.peek(), Some(&6));
|
||||
assert_eq!(mp.peeking_next(|&x| x != 5), None);
|
||||
assert_eq!(mp.peek(), Some(&7));
|
||||
assert_eq!(mp.peeking_next(|&x| x == 5), Some(5));
|
||||
assert_eq!(mp.peeking_next(|&x| x == 6), Some(6));
|
||||
assert_eq!(mp.peek(), Some(&7));
|
||||
assert_eq!(mp.peek(), None);
|
||||
assert_eq!(mp.next(), Some(7));
|
||||
assert_eq!(mp.peek(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -524,13 +363,6 @@ fn pad_using() {
|
|||
it::assert_equal(r, vec![0, 1, 2]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn while_some() {
|
||||
let ns = (1..10).map(|x| if x % 5 != 0 { Some(x) } else { None })
|
||||
.while_some();
|
||||
it::assert_equal(ns, vec![1, 2, 3, 4]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn group_by() {
|
||||
for (ch1, sub) in &"AABBCCC".chars().group_by(|&x| x) {
|
||||
|
@ -693,6 +525,18 @@ fn chunks() {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn concat_empty() {
|
||||
let data: Vec<Vec<()>> = Vec::new();
|
||||
assert_eq!(data.into_iter().concat(), Vec::new())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn concat_non_empty() {
|
||||
let data = vec![vec![1,2,3], vec![4,5,6], vec![7,8,9]];
|
||||
assert_eq!(data.into_iter().concat(), vec![1,2,3,4,5,6,7,8,9])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flatten_iter() {
|
||||
let data = vec![vec![1,2,3], vec![4,5,6]];
|
||||
|
@ -701,26 +545,6 @@ fn flatten_iter() {
|
|||
it::assert_equal(flattened, vec![1,2,3,4,5,6]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flatten_rev() {
|
||||
let data = vec![vec![1,2,3].into_iter(), vec![4,5,6].into_iter()];
|
||||
let flattened = data.into_iter().flatten().rev();
|
||||
it::assert_equal(flattened, vec![6,5,4,3,2,1]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flatten_clone() {
|
||||
let data = &[
|
||||
&[1,2,3],
|
||||
&[4,5,6]
|
||||
];
|
||||
let flattened1 = data.into_iter().cloned().flatten();
|
||||
let flattened2 = flattened1.clone();
|
||||
|
||||
it::assert_equal(flattened1, &[1,2,3,4,5,6]);
|
||||
it::assert_equal(flattened2, &[1,2,3,4,5,6]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn flatten_fold() {
|
||||
let xs = [0, 1, 1, 1, 2, 1, 3, 3];
|
||||
|
@ -770,10 +594,9 @@ fn combinations_of_too_short() {
|
|||
}
|
||||
|
||||
|
||||
#[should_panic]
|
||||
#[test]
|
||||
fn combinations_zero() {
|
||||
(1..3).combinations(0);
|
||||
it::assert_equal((1..3).combinations(0), vec![vec![]]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -817,23 +640,6 @@ fn diff_shorter() {
|
|||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fold_while() {
|
||||
let mut iterations = 0;
|
||||
let vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
let sum = vec.into_iter().fold_while(0, |acc, item| {
|
||||
iterations += 1;
|
||||
let new_sum = acc.clone() + item;
|
||||
if new_sum <= 20 {
|
||||
FoldWhile::Continue(new_sum)
|
||||
} else {
|
||||
FoldWhile::Done(acc)
|
||||
}
|
||||
});
|
||||
assert_eq!(iterations, 6);
|
||||
assert_eq!(sum, 15);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn minmax() {
|
||||
use std::cmp::Ordering;
|
||||
|
@ -889,3 +695,28 @@ fn format() {
|
|||
let t3 = format!("{:.2e}", dataf.iter().format(", "));
|
||||
assert_eq!(t3, "1.10e0, 2.72e0, -2.20e1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn while_some() {
|
||||
let ns = (1..10).map(|x| if x % 5 != 0 { Some(x) } else { None })
|
||||
.while_some();
|
||||
it::assert_equal(ns, vec![1, 2, 3, 4]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fold_while() {
|
||||
let mut iterations = 0;
|
||||
let vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||
let sum = vec.into_iter().fold_while(0, |acc, item| {
|
||||
iterations += 1;
|
||||
let new_sum = acc.clone() + item;
|
||||
if new_sum <= 20 {
|
||||
FoldWhile::Continue(new_sum)
|
||||
} else {
|
||||
FoldWhile::Done(acc)
|
||||
}
|
||||
}).into_inner();
|
||||
assert_eq!(iterations, 6);
|
||||
assert_eq!(sum, 15);
|
||||
}
|
||||
|
|
@ -71,3 +71,18 @@ fn next_tuple() {
|
|||
assert_eq!(iter.next_tuple().map(|(&x, &y)| (x, y)), Some((3, 4)));
|
||||
assert_eq!(iter.next_tuple::<(_, _)>(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn collect_tuple() {
|
||||
let v = [1, 2];
|
||||
let iter = v.iter().cloned();
|
||||
assert_eq!(iter.collect_tuple(), Some((1, 2)));
|
||||
|
||||
let v = [1];
|
||||
let iter = v.iter().cloned();
|
||||
assert_eq!(iter.collect_tuple::<(_, _)>(), None);
|
||||
|
||||
let v = [1, 2, 3];
|
||||
let iter = v.iter().cloned();
|
||||
assert_eq!(iter.collect_tuple::<(_, _)>(), None);
|
||||
}
|
||||
|
|
|
@ -5,12 +5,11 @@ use itertools::EitherOrBoth::{Both, Left, Right};
|
|||
use itertools::free::zip_eq;
|
||||
|
||||
#[test]
|
||||
fn zip_longest_fused()
|
||||
{
|
||||
fn zip_longest_fused() {
|
||||
let a = [Some(1), None, Some(3), Some(4)];
|
||||
let b = [1, 2, 3];
|
||||
|
||||
let unfused = a.iter().batching(|mut it| *it.next().unwrap())
|
||||
let unfused = a.iter().batching(|it| *it.next().unwrap())
|
||||
.zip_longest(b.iter().cloned());
|
||||
itertools::assert_equal(unfused,
|
||||
vec![Both(1, 1), Right(2), Right(3)]);
|
||||
|
|
|
@ -733,7 +733,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.5.10"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"either 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1378,7 +1378,7 @@ dependencies = [
|
|||
"fallible 0.0.1",
|
||||
"fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hashglobe 0.1.0",
|
||||
"itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1860,7 +1860,7 @@ dependencies = [
|
|||
"checksum ident_case 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c9826188e666f2ed92071d2dadef6edc430b11b158b5b2b3f4babbcc891eaaa"
|
||||
"checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d"
|
||||
"checksum iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29d062ee61fccdf25be172e70f34c9f6efc597e1fb8f6526e8437b2046ab26be"
|
||||
"checksum itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4833d6978da405305126af4ac88569b5d71ff758581ce5a987dbfa3755f694fc"
|
||||
"checksum itertools 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b07332223953b5051bceb67e8c4700aa65291535568e1f12408c43c4a42c0394"
|
||||
"checksum itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2f404fbc66fd9aac13e998248505e7ecb2ad8e44ab6388684c5fb11c6c251c"
|
||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
"checksum khronos_api 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d867c645cfeb8a7fec503731679eac03ac11b7105aa5a71cb8f8ee5271636add"
|
||||
|
|
|
@ -731,7 +731,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.5.10"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"either 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1369,7 +1369,7 @@ dependencies = [
|
|||
"fallible 0.0.1",
|
||||
"fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hashglobe 0.1.0",
|
||||
"itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itertools 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1863,7 +1863,7 @@ dependencies = [
|
|||
"checksum ident_case 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c9826188e666f2ed92071d2dadef6edc430b11b158b5b2b3f4babbcc891eaaa"
|
||||
"checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d"
|
||||
"checksum iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29d062ee61fccdf25be172e70f34c9f6efc597e1fb8f6526e8437b2046ab26be"
|
||||
"checksum itertools 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4833d6978da405305126af4ac88569b5d71ff758581ce5a987dbfa3755f694fc"
|
||||
"checksum itertools 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b07332223953b5051bceb67e8c4700aa65291535568e1f12408c43c4a42c0394"
|
||||
"checksum itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2f404fbc66fd9aac13e998248505e7ecb2ad8e44ab6388684c5fb11c6c251c"
|
||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
"checksum khronos_api 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d867c645cfeb8a7fec503731679eac03ac11b7105aa5a71cb8f8ee5271636add"
|
||||
|
|
Загрузка…
Ссылка в новой задаче