Skip to content

Commit db54f58

Browse files
committed
Allow custom collections for multi cartesian product items
Allow `MultiProduct` to yield collections other than `Vec`. The main value is that callers can specify non-allocating types, meaning that iteration over a multi-cartesian product can be non-allocating. Creating the product itself is still an allocation, however. To not be a breaking change, the `multi_cartesian_product` method still returns a product iterating over `Vec` (since method generics can not specify defaults), and a new generic method `multi_cartesian_product_with` is added.
1 parent 70988ff commit db54f58

File tree

2 files changed

+62
-29
lines changed

2 files changed

+62
-29
lines changed

src/adaptors/multi_product.rs

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,35 @@
11
#![cfg(feature = "use_std")]
22

3+
use std::iter::FromIterator;
4+
use std::marker::PhantomData;
35
use size_hint;
4-
use Itertools;
56

67
#[derive(Clone)]
78
/// An iterator adaptor that iterates over the cartesian product of
89
/// multiple iterators of type `I`.
910
///
10-
/// An iterator element type is `Vec<I>`.
11+
/// An iterator element type is `T<I>`, which defaults to `Vec<I>`.
1112
///
1213
/// See [`.multi_cartesian_product()`](../trait.Itertools.html#method.multi_cartesian_product)
1314
/// for more information.
1415
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
15-
pub struct MultiProduct<I>(Vec<MultiProductIter<I>>)
16+
pub struct MultiProduct<I, T=Vec<<I as Iterator>::Item>>(Vec<MultiProductIter<I>>, PhantomData<T>)
1617
where I: Iterator + Clone,
17-
I::Item: Clone;
18+
I::Item: Clone,
19+
T: FromIterator<I::Item>;
1820

1921
/// Create a new cartesian product iterator over an arbitrary number
2022
/// of iterators of the same type.
2123
///
22-
/// Iterator element is of type `Vec<H::Item::Item>`.
23-
pub fn multi_cartesian_product<H>(iters: H) -> MultiProduct<<H::Item as IntoIterator>::IntoIter>
24+
/// Iterator element is of type `T<H::Item::Item>`.
25+
pub fn multi_cartesian_product<H, T>(iters: H) -> MultiProduct<<H::Item as IntoIterator>::IntoIter, T>
2426
where H: Iterator,
2527
H::Item: IntoIterator,
2628
<H::Item as IntoIterator>::IntoIter: Clone,
27-
<H::Item as IntoIterator>::Item: Clone
29+
<H::Item as IntoIterator>::Item: Clone,
30+
T: FromIterator<<H::Item as IntoIterator>::Item>
2831
{
29-
MultiProduct(iters.map(|i| MultiProductIter::new(i.into_iter())).collect())
32+
MultiProduct(iters.map(|i| MultiProductIter::new(i.into_iter())).collect(), PhantomData)
3033
}
3134

3235
#[derive(Clone, Debug)]
@@ -47,9 +50,10 @@ enum MultiProductIterState {
4750
MidIter { on_first_iter: bool },
4851
}
4952

50-
impl<I> MultiProduct<I>
53+
impl<I, T> MultiProduct<I, T>
5154
where I: Iterator + Clone,
52-
I::Item: Clone
55+
I::Item: Clone,
56+
T: FromIterator<I::Item>
5357
{
5458
/// Iterates the rightmost iterator, then recursively iterates iterators
5559
/// to the left if necessary.
@@ -77,7 +81,7 @@ impl<I> MultiProduct<I>
7781

7882
if last.in_progress() {
7983
true
80-
} else if MultiProduct::iterate_last(rest, state) {
84+
} else if Self::iterate_last(rest, state) {
8185
last.reset();
8286
last.iterate();
8387
// If iterator is None twice consecutively, then iterator is
@@ -97,7 +101,7 @@ impl<I> MultiProduct<I>
97101
}
98102

99103
/// Returns the unwrapped value of the next iteration.
100-
fn curr_iterator(&self) -> Vec<I::Item> {
104+
fn curr_iterator(&self) -> T {
101105
self.0.iter().map(|multi_iter| {
102106
multi_iter.cur.clone().unwrap()
103107
}).collect()
@@ -143,14 +147,15 @@ impl<I> MultiProductIter<I>
143147
}
144148
}
145149

146-
impl<I> Iterator for MultiProduct<I>
150+
impl<I, T> Iterator for MultiProduct<I, T>
147151
where I: Iterator + Clone,
148-
I::Item: Clone
152+
I::Item: Clone,
153+
T: FromIterator<I::Item>
149154
{
150-
type Item = Vec<I::Item>;
155+
type Item = T;
151156

152157
fn next(&mut self) -> Option<Self::Item> {
153-
if MultiProduct::iterate_last(
158+
if Self::iterate_last(
154159
&mut self.0,
155160
MultiProductIterState::StartOfIter
156161
) {
@@ -204,17 +209,8 @@ impl<I> Iterator for MultiProduct<I>
204209
}
205210

206211
fn last(self) -> Option<Self::Item> {
207-
let iter_count = self.0.len();
208-
209-
let lasts: Self::Item = self.0.into_iter()
212+
self.0.into_iter()
210213
.map(|multi_iter| multi_iter.iter.last())
211-
.while_some()
212-
.collect();
213-
214-
if lasts.len() == iter_count {
215-
Some(lasts)
216-
} else {
217-
None
218-
}
214+
.collect()
219215
}
220216
}

src/lib.rs

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -890,11 +890,48 @@ pub trait Itertools : Iterator {
890890
/// assert_eq!(multi_prod.next(), None);
891891
/// ```
892892
#[cfg(feature = "use_std")]
893-
fn multi_cartesian_product(self) -> MultiProduct<<Self::Item as IntoIterator>::IntoIter>
893+
fn multi_cartesian_product(self) -> MultiProduct<<Self::Item as IntoIterator>::IntoIter, Vec<<Self::Item as IntoIterator>::Item>>
894894
where Self: Iterator + Sized,
895895
Self::Item: IntoIterator,
896896
<Self::Item as IntoIterator>::IntoIter: Clone,
897-
<Self::Item as IntoIterator>::Item: Clone
897+
<Self::Item as IntoIterator>::Item: Clone,
898+
{
899+
self.multi_cartesian_product_with()
900+
}
901+
902+
/// Return an iterator adaptor that iterates over the cartesian product of
903+
/// all subiterators returned by meta-iterator `self`.
904+
///
905+
/// All provided iterators must yield the same `Item` type. To generate
906+
/// the product of iterators yielding multiple types, use the
907+
/// [`iproduct`](macro.iproduct.html) macro instead.
908+
///
909+
///
910+
/// The iterator element type is `C<T>`, where `T` is the iterator element
911+
/// of the subiterators.
912+
///
913+
/// ```
914+
/// use std::collections::VecDeque;
915+
/// use itertools::Itertools;
916+
/// let mut multi_prod = (0..3).map(|i| (i * 2)..(i * 2 + 2))
917+
/// .multi_cartesian_product_with::<VecDeque<i32>>();
918+
/// assert_eq!(multi_prod.next(), Some(vec![0, 2, 4].into()));
919+
/// assert_eq!(multi_prod.next(), Some(vec![0, 2, 5].into()));
920+
/// assert_eq!(multi_prod.next(), Some(vec![0, 3, 4].into()));
921+
/// assert_eq!(multi_prod.next(), Some(vec![0, 3, 5].into()));
922+
/// assert_eq!(multi_prod.next(), Some(vec![1, 2, 4].into()));
923+
/// assert_eq!(multi_prod.next(), Some(vec![1, 2, 5].into()));
924+
/// assert_eq!(multi_prod.next(), Some(vec![1, 3, 4].into()));
925+
/// assert_eq!(multi_prod.next(), Some(vec![1, 3, 5].into()));
926+
/// assert_eq!(multi_prod.next(), None);
927+
/// ```
928+
#[cfg(feature = "use_std")]
929+
fn multi_cartesian_product_with<C>(self) -> MultiProduct<<Self::Item as IntoIterator>::IntoIter, C>
930+
where Self: Iterator + Sized,
931+
Self::Item: IntoIterator,
932+
<Self::Item as IntoIterator>::IntoIter: Clone,
933+
<Self::Item as IntoIterator>::Item: Clone,
934+
C: FromIterator<<Self::Item as IntoIterator>::Item>,
898935
{
899936
adaptors::multi_cartesian_product(self)
900937
}

0 commit comments

Comments
 (0)