Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
/target/
Cargo.lock
Cargo.lock
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you re-add the final line break, you can remove this file from the diff entirely (or just git checkout master -- .gitignore)

36 changes: 36 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ pub mod structs {
pub use peeking_take_while::PeekingTakeWhile;
pub use process_results_impl::ProcessResults;
#[cfg(feature = "use_std")]
pub use product_combination::ProductCombination;
#[cfg(feature = "use_std")]
pub use put_back_n_impl::PutBackN;
#[cfg(feature = "use_std")]
pub use rciter_impl::RcIter;
Expand Down Expand Up @@ -145,6 +147,8 @@ mod pad_tail;
mod peeking_take_while;
mod process_results_impl;
#[cfg(feature = "use_std")]
mod product_combination;
#[cfg(feature = "use_std")]
mod put_back_n_impl;
#[cfg(feature = "use_std")]
mod rciter_impl;
Expand Down Expand Up @@ -1983,6 +1987,38 @@ pub trait Itertools : Iterator {
|x, y, _, _| Ordering::Less == compare(x, y)
)
}

/// Returns an iterator adaptor that iterators over all product combinations of the elements from the iterator.
///
/// Every iteration produces a new `Vec` containing cloned elements from the iterator. The iterator will never halt as there are infinitely many product combinations.
///
///```
///use itertools::Itertools;
///
///let list = vec![1, 2, 3];
///
///let mut iter = list.iter().product_combination();
///
///assert_eq!(iter.next().unwrap(), vec![&1]);
///assert_eq!(iter.next().unwrap(), vec![&2]);
///assert_eq!(iter.next().unwrap(), vec![&3]);
///assert_eq!(iter.next().unwrap(), vec![&1, &1]);
///assert_eq!(iter.next().unwrap(), vec![&2, &1]);
///assert_eq!(iter.next().unwrap(), vec![&3, &1]);
///assert_eq!(iter.next().unwrap(), vec![&1, &2]);
///assert_eq!(iter.next().unwrap(), vec![&2, &2]);
///assert_eq!(iter.next().unwrap(), vec![&3, &2]);
///assert_eq!(iter.next().unwrap(), vec![&1, &3]);
///assert_eq!(iter.next().unwrap(), vec![&2, &3]);
///assert_eq!(iter.next().unwrap(), vec![&3, &3]);
///assert_eq!(iter.next().unwrap(), vec![&1, &1, &1]);
///```
#[cfg(feature = "use_std")]
fn product_combination(self) -> product_combination::ProductCombination<Self>
where Self: Sized + Clone
{
product_combination::product_combination(self)
}
}

impl<T: ?Sized> Itertools for T where T: Iterator { }
Expand Down
80 changes: 80 additions & 0 deletions src/product_combination.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/// An iterator to iterate through all product combinations of an iterator.
///
/// See [`.product_combination()`](../trait.Itertools.html#method.product_combination) for more information.
pub struct ProductCombination<I: Iterator + Clone> {
data: I,
//Internal state is a vec of tuples containing an iterator and the last retrieved item from this iterator
internal_state: Vec<(I, I::Item)>
}

/// Create a new `ProductionCombination` from a clonable iterator.
pub fn product_combination<I>(iter: I) -> ProductCombination<I>
where I: Iterator + Clone
{
ProductCombination {
data: iter,
internal_state: Vec::new(),
}
}

impl<I: Iterator + Clone> ProductCombination<I> {
fn increment(&mut self) {
let mut pointer: usize = 0;
let mut tack_on = false;
loop {
match self.internal_state.get_mut(pointer) {
None => {
tack_on = true;
break;
},
Some(ref mut pair) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't use default match binding in Itertools since it's still on Rust 1.12, but the tuple deref/item re-ref can still be done within the pattern match:

Some(&mut (ref mut iter, ref mut item)) => {

let iter = &mut pair.0;
let item = &mut pair.1;

let new_item = iter.next();
match new_item {
//Reached the end of the iterator ... restart the iterator and advance the pointer
None => {
*iter = self.data.clone();
match iter.next() {
None => {
panic!("0 sized iterator");
},
Some(i) => {
*item = i;
}
}
pointer += 1;
},
Some(i) => {
*item = i;
break;
}
}
},
}
}
if tack_on {
let mut new_iter = self.data.clone();
match new_iter.next() {
None => {
panic!("0 sized iterator");
},
Some(new_item) => {
self.internal_state.push((new_iter, new_item));
}
}
}
}
}

impl<I> Iterator for ProductCombination<I>
where I: Iterator + Clone, I::Item: Clone
{
type Item = Vec<I::Item>;

fn next(&mut self) -> Option<Self::Item> {
self.increment();
Some(self.internal_state.iter().map(|ref pair| pair.1.clone()).collect())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The item yielded by iter() is already a reference; you don't need to use ref in the closure. Also, the item can be obtained with a pattern match:

.map(|&(_, ref item)| item.clone())

}
}