-
Notifications
You must be signed in to change notification settings - Fork 319
add array_permutations()
#1013
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add array_permutations()
#1013
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,29 +1,39 @@ | ||
use alloc::boxed::Box; | ||
use alloc::vec::Vec; | ||
use core::marker::PhantomData; | ||
use std::fmt; | ||
use std::iter::once; | ||
use std::iter::FusedIterator; | ||
|
||
use super::lazy_buffer::LazyBuffer; | ||
use crate::size_hint::{self, SizeHint}; | ||
|
||
/// Iterator for `Vec` valued permutations returned by | ||
/// [`.permutations()`](crate::Itertools::permutations) | ||
pub type ArrayPermutations<I, const K: usize> = PermutationsGeneric<I, [<I as Iterator>::Item; K]>; | ||
/// Iterator for const generic permutations returned by | ||
/// [`.array_permutations()`](crate::Itertools::array_permutations) | ||
pub type Permutations<I> = PermutationsGeneric<I, Vec<<I as Iterator>::Item>>; | ||
|
||
/// An iterator adaptor that iterates through all the `k`-permutations of the | ||
/// elements from an iterator. | ||
/// | ||
/// See [`.permutations()`](crate::Itertools::permutations) for | ||
/// See [`.permutations()`](crate::Itertools::permutations) and | ||
/// [`.array_permutations()`](crate::Itertools::array_permutations) for | ||
/// more information. | ||
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] | ||
pub struct Permutations<I: Iterator> { | ||
pub struct PermutationsGeneric<I: Iterator, Item> { | ||
vals: LazyBuffer<I>, | ||
state: PermutationState, | ||
_item: PhantomData<Item>, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the decision There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With the current algorithm, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, using a |
||
} | ||
|
||
impl<I> Clone for Permutations<I> | ||
impl<I, Item> Clone for PermutationsGeneric<I, Item> | ||
where | ||
I: Clone + Iterator, | ||
I::Item: Clone, | ||
{ | ||
clone_fields!(vals, state); | ||
clone_fields!(vals, state, _item); | ||
} | ||
|
||
#[derive(Clone, Debug)] | ||
|
@@ -41,34 +51,44 @@ enum PermutationState { | |
End, | ||
} | ||
|
||
impl<I> fmt::Debug for Permutations<I> | ||
impl<I, Item> fmt::Debug for PermutationsGeneric<I, Item> | ||
where | ||
I: Iterator + fmt::Debug, | ||
I::Item: fmt::Debug, | ||
{ | ||
debug_fmt_fields!(Permutations, vals, state); | ||
} | ||
|
||
pub fn array_permutations<I: Iterator, const K: usize>(iter: I) -> ArrayPermutations<I, K> { | ||
PermutationsGeneric { | ||
vals: LazyBuffer::new(iter), | ||
state: PermutationState::Start { k: K }, | ||
_item: PhantomData, | ||
} | ||
} | ||
|
||
pub fn permutations<I: Iterator>(iter: I, k: usize) -> Permutations<I> { | ||
Permutations { | ||
PermutationsGeneric { | ||
vals: LazyBuffer::new(iter), | ||
state: PermutationState::Start { k }, | ||
_item: PhantomData, | ||
} | ||
} | ||
|
||
impl<I> Iterator for Permutations<I> | ||
impl<I, Item> Iterator for PermutationsGeneric<I, Item> | ||
where | ||
I: Iterator, | ||
I::Item: Clone, | ||
Item: PermItem<I::Item>, | ||
{ | ||
type Item = Vec<I::Item>; | ||
type Item = Item; | ||
|
||
fn next(&mut self) -> Option<Self::Item> { | ||
let Self { vals, state } = self; | ||
let Self { vals, state, _item } = self; | ||
match state { | ||
PermutationState::Start { k: 0 } => { | ||
*state = PermutationState::End; | ||
Some(Vec::new()) | ||
Some(Item::extract_start(vals, 0, 0)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see calling There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The issue is that whatever code goes here needs to generically produce a impl PoolIndex for [usize; K]{
...
fn empty_item() -> Option<Self::Item> {
if K == 0 {
Some([])
} else {
None
}
}
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct, this attempt won't work. However, we could introduce |
||
} | ||
&mut PermutationState::Start { k } => { | ||
vals.prefill(k); | ||
|
@@ -77,14 +97,11 @@ where | |
return None; | ||
} | ||
*state = PermutationState::Buffered { k, min_n: k }; | ||
Some(vals[0..k].to_vec()) | ||
Some(Item::extract_start(vals, k, k - 1)) | ||
} | ||
PermutationState::Buffered { ref k, min_n } => { | ||
if vals.get_next() { | ||
let item = (0..*k - 1) | ||
.chain(once(*min_n)) | ||
.map(|i| vals[i].clone()) | ||
.collect(); | ||
let item = Item::extract_start(vals, *k, *min_n); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Having a separate variant |
||
*min_n += 1; | ||
Some(item) | ||
} else { | ||
|
@@ -99,7 +116,7 @@ where | |
return None; | ||
} | ||
} | ||
let item = vals.get_at(&indices[0..*k]); | ||
let item = Item::extract_from_prefix(vals, &indices[0..*k]); | ||
*state = PermutationState::Loaded { indices, cycles }; | ||
Some(item) | ||
} | ||
|
@@ -110,14 +127,14 @@ where | |
return None; | ||
} | ||
let k = cycles.len(); | ||
Some(vals.get_at(&indices[0..k])) | ||
Some(Item::extract_from_prefix(vals, &indices[0..k])) | ||
} | ||
PermutationState::End => None, | ||
} | ||
} | ||
|
||
fn count(self) -> usize { | ||
let Self { vals, state } = self; | ||
let Self { vals, state, _item } = self; | ||
let n = vals.count(); | ||
state.size_hint_for(n).1.unwrap() | ||
} | ||
|
@@ -130,10 +147,11 @@ where | |
} | ||
} | ||
|
||
impl<I> FusedIterator for Permutations<I> | ||
impl<I, Item> FusedIterator for PermutationsGeneric<I, Item> | ||
where | ||
I: Iterator, | ||
I::Item: Clone, | ||
Item: PermItem<I::Item>, | ||
{ | ||
} | ||
|
||
|
@@ -184,3 +202,39 @@ impl PermutationState { | |
} | ||
} | ||
} | ||
|
||
/// A type that can be picked out from a pool or buffer of items from an inner iterator | ||
/// and in a generic way given their indices. | ||
pub trait PermItem<T> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this new trait really necessary? Couldn't we re-use/generalize/extend There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is what I started with but it seems that the operations required for |
||
fn extract_start<I: Iterator<Item = T>>(buf: &LazyBuffer<I>, len: usize, last: usize) -> Self; | ||
|
||
fn extract_from_prefix<I: Iterator<Item = T>>(buf: &LazyBuffer<I>, indices: &[usize]) -> Self; | ||
} | ||
|
||
impl<T: Clone, const K: usize> PermItem<T> for [T; K] { | ||
fn extract_start<I: Iterator<Item = T>>(buf: &LazyBuffer<I>, len: usize, last: usize) -> Self { | ||
assert_eq!(len, K); | ||
buf.get_array_from_fn(|i| if i + 1 < len { i } else { last }) | ||
} | ||
|
||
fn extract_from_prefix<I: Iterator<Item = T>>(buf: &LazyBuffer<I>, indices: &[usize]) -> Self { | ||
buf.get_array_from_fn(|i| indices[i]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't this essentially There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that when this function is called, there does not exist an instance of |
||
} | ||
} | ||
|
||
impl<T: Clone> PermItem<T> for Vec<T> { | ||
fn extract_start<I: Iterator<Item = T>>(buf: &LazyBuffer<I>, len: usize, last: usize) -> Self { | ||
if len == 0 { | ||
Vec::new() | ||
} else { | ||
(0..len - 1) | ||
.chain(once(last)) | ||
.map(|i| buf[i].clone()) | ||
.collect() | ||
} | ||
Comment on lines
+227
to
+234
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Special cases that might go away if we always worked on |
||
} | ||
|
||
fn extract_from_prefix<I: Iterator<Item = T>>(buf: &LazyBuffer<I>, indices: &[usize]) -> Self { | ||
buf.get_at(indices) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having the second generic parameter
Vec<<I as Iterator>::Item>
seems odd whenPermutationsGeneric
has[usize; K]
.[Array]Combinations
haveVec<usize>
and[usize; K]
, respectively. Unless there's a good reason, we should adopt this pattern.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was an error,
array_permutations()
was only working onIterator<Item = usize>
before. Fixed now.