Skip to content

Commit 29a7bbd

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 29a7bbd

File tree

2 files changed

+61
-29
lines changed

2 files changed

+61
-29
lines changed

src/adaptors/multi_product.rs

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

3+
use std::iter::FromIterator;
34
use size_hint;
4-
use Itertools;
55

66
#[derive(Clone)]
77
/// An iterator adaptor that iterates over the cartesian product of
88
/// multiple iterators of type `I`.
99
///
10-
/// An iterator element type is `Vec<I>`.
10+
/// An iterator element type is `T<I>`, which defaults to `Vec<I>`.
1111
///
1212
/// See [`.multi_cartesian_product()`](../trait.Itertools.html#method.multi_cartesian_product)
1313
/// for more information.
1414
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
15-
pub struct MultiProduct<I>(Vec<MultiProductIter<I>>)
15+
pub struct MultiProduct<I, T=Vec<<I as Iterator>::Item>>(Vec<MultiProductIter<I>>, std::marker::PhantomData<T>)
1616
where I: Iterator + Clone,
17-
I::Item: Clone;
17+
I::Item: Clone,
18+
T: FromIterator<I::Item>;
1819

1920
/// Create a new cartesian product iterator over an arbitrary number
2021
/// of iterators of the same type.
2122
///
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>
23+
/// Iterator element is of type `T<H::Item::Item>`.
24+
pub fn multi_cartesian_product<H, T>(iters: H) -> MultiProduct<<H::Item as IntoIterator>::IntoIter, T>
2425
where H: Iterator,
2526
H::Item: IntoIterator,
2627
<H::Item as IntoIterator>::IntoIter: Clone,
27-
<H::Item as IntoIterator>::Item: Clone
28+
<H::Item as IntoIterator>::Item: Clone,
29+
T: FromIterator<<H::Item as IntoIterator>::Item>
2830
{
29-
MultiProduct(iters.map(|i| MultiProductIter::new(i.into_iter())).collect())
31+
MultiProduct(iters.map(|i| MultiProductIter::new(i.into_iter())).collect(), std::marker::PhantomData)
3032
}
3133

3234
#[derive(Clone, Debug)]
@@ -47,9 +49,10 @@ enum MultiProductIterState {
4749
MidIter { on_first_iter: bool },
4850
}
4951

50-
impl<I> MultiProduct<I>
52+
impl<I, T> MultiProduct<I, T>
5153
where I: Iterator + Clone,
52-
I::Item: Clone
54+
I::Item: Clone,
55+
T: FromIterator<I::Item>
5356
{
5457
/// Iterates the rightmost iterator, then recursively iterates iterators
5558
/// to the left if necessary.
@@ -77,7 +80,7 @@ impl<I> MultiProduct<I>
7780

7881
if last.in_progress() {
7982
true
80-
} else if MultiProduct::iterate_last(rest, state) {
83+
} else if Self::iterate_last(rest, state) {
8184
last.reset();
8285
last.iterate();
8386
// If iterator is None twice consecutively, then iterator is
@@ -97,7 +100,7 @@ impl<I> MultiProduct<I>
97100
}
98101

99102
/// Returns the unwrapped value of the next iteration.
100-
fn curr_iterator(&self) -> Vec<I::Item> {
103+
fn curr_iterator(&self) -> T {
101104
self.0.iter().map(|multi_iter| {
102105
multi_iter.cur.clone().unwrap()
103106
}).collect()
@@ -143,14 +146,15 @@ impl<I> MultiProductIter<I>
143146
}
144147
}
145148

146-
impl<I> Iterator for MultiProduct<I>
149+
impl<I, T> Iterator for MultiProduct<I, T>
147150
where I: Iterator + Clone,
148-
I::Item: Clone
151+
I::Item: Clone,
152+
T: FromIterator<I::Item>
149153
{
150-
type Item = Vec<I::Item>;
154+
type Item = T;
151155

152156
fn next(&mut self) -> Option<Self::Item> {
153-
if MultiProduct::iterate_last(
157+
if Self::iterate_last(
154158
&mut self.0,
155159
MultiProductIterState::StartOfIter
156160
) {
@@ -204,17 +208,8 @@ impl<I> Iterator for MultiProduct<I>
204208
}
205209

206210
fn last(self) -> Option<Self::Item> {
207-
let iter_count = self.0.len();
208-
209-
let lasts: Self::Item = self.0.into_iter()
211+
self.0.into_iter()
210212
.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-
}
213+
.collect()
219214
}
220215
}

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)