Skip to content

Commit 9027ef0

Browse files
committed
Distinguish different degenerated cases.
1 parent 8c5f630 commit 9027ef0

File tree

2 files changed

+28
-11
lines changed

2 files changed

+28
-11
lines changed

src/adaptors/mod.rs

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -373,19 +373,24 @@ pub enum Power<I>
373373
where
374374
I: Iterator,
375375
{
376-
/// The adaptor is empty if either:
377-
/// - pow is 0
378-
/// - the original iterator is empty
379-
/// - the adaptor is exhausted
376+
/// The adaptor is 'Empty' if either:
377+
/// - The original iterator is empty.
378+
/// - The adaptor is exhausted.
380379
Empty,
381-
/// Otherwise, there will be items yielded.
380+
/// The adaptor is 'Degenerated' if the given pow == 0.
381+
/// In this case, it yields 1 empty item before switching to Empty itself.
382+
/// This ensures that cartesian_power(it, n).count() == it.count().pow(n) for all n.
383+
/// In the case it.is_empty() && n == 0, we choose convention 0^0=1.
384+
Degenerated,
385+
/// In any other case, there will be non-empty items yielded.
382386
Filled {
383387
/// Copy of the original iterator, never consumed.
384388
original: I,
385-
/// Clones of the original iterator, scrolled at various speeds
389+
/// Clones of the original iterator,
390+
/// scrolled at various speeds from left to right
386391
/// and regularly replaced by fresh clones once exhausted.
387392
clones: Vec<I>,
388-
/// Current state of the iterator: the next item to be yielded.
393+
/// Current state of the iterator: the next non-empty item to be yielded.
389394
state: Vec<I::Item>,
390395
},
391396
}
@@ -399,14 +404,13 @@ where
399404
I::Item: Clone,
400405
{
401406
match pow {
402-
// Trivial case.
403-
0 => Power::Empty,
407+
0 => Power::Degenerated,
404408
pow => {
405409
// Test one clone first to determine whether
406410
// some values are actually yielded by the given iterator.
407411
let mut first_clone = it.clone();
408412
match first_clone.next() {
409-
// New trivial case: the given iterator is empty.
413+
// No item will be yielded if the iterator is empty.
410414
None => Power::Empty,
411415
Some(first_state) => {
412416
// Produce other clones until we have `pow` of them.
@@ -471,13 +475,19 @@ where
471475
}
472476
Some(res)
473477
}
474-
// Check trivial case last as it should be less frequent.
478+
// Check les frequent cases last.
475479
Power::Empty => None,
480+
Power::Degenerated => {
481+
// Yield One empty item and get exhausted.
482+
*self = Power::Empty;
483+
Some(Vec::new())
484+
}
476485
}
477486
}
478487

479488
fn size_hint(&self) -> (usize, Option<usize>) {
480489
match self {
490+
Power::Degenerated => (1, Some(1)),
481491
Power::Empty => (0, Some(0)),
482492
Power::Filled {
483493
original, clones, ..

src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1003,6 +1003,13 @@ pub trait Itertools : Iterator {
10031003
/// ['b', 'b', 'b'],
10041004
/// ],
10051005
/// );
1006+
///
1007+
/// // Watch various possible degenerated cases:
1008+
/// let empty_result: Vec<Vec<char>> = vec![];
1009+
/// let singleton_result: Vec<Vec<char>> = vec![vec![]];
1010+
/// itertools::assert_equal("".chars().cartesian_power(3), empty_result.clone());
1011+
/// itertools::assert_equal("ab".chars().cartesian_power(0), singleton_result.clone());
1012+
/// itertools::assert_equal("".chars().cartesian_power(0), singleton_result.clone());
10061013
/// ```
10071014
fn cartesian_power(self, pow: usize) -> Power<Self>
10081015
where

0 commit comments

Comments
 (0)