同时迭代多个变量可以很有用,可以重叠(slice::windows
),也可以不重叠(slice::chunks
)。
这仅适用于切片;是否可能对使用元组方便的迭代器执行此操作?
可以编写类似以下内容的代码:
for (prev, next) in some_iter.windows(2) {
...
}
如果不行,那么它是否可以作为现有迭代器的特性来实现?
同时迭代多个变量可以很有用,可以重叠(slice::windows
),也可以不重叠(slice::chunks
)。
这仅适用于切片;是否可能对使用元组方便的迭代器执行此操作?
可以编写类似以下内容的代码:
for (prev, next) in some_iter.windows(2) {
...
}
如果不行,那么它是否可以作为现有迭代器的特性来实现?
可以使用Itertools::tuples
从迭代器中获取一些块,最多可达到4个元组:
use itertools::Itertools; // 0.9.0
fn main() {
let some_iter = vec![1, 2, 3, 4, 5, 6].into_iter();
for (prev, next) in some_iter.tuples() {
println!("{}--{}", prev, next);
}
}
1--2
3--4
5--6
如果您不知道您的迭代器是否完全适合块中,您可以使用Tuples::into_buffer
来访问任何剩余部分:
use itertools::Itertools; // 0.9.0
fn main() {
let some_iter = vec![1, 2, 3, 4, 5].into_iter();
let mut t = some_iter.tuples();
for (prev, next) in t.by_ref() {
println!("{}--{}", prev, next);
}
for leftover in t.into_buffer() {
println!("{}", leftover);
}
}
1--2
3--4
5
使用Itertools::tuple_windows
,也可以使用4元组窗口:
use itertools::Itertools; // 0.9.0
fn main() {
let some_iter = vec![1, 2, 3, 4, 5, 6].into_iter();
for (prev, next) in some_iter.tuple_windows() {
println!("{}--{}", prev, next);
}
}
1--2
2--3
3--4
4--5
5--6
chunks
和 windows
的最佳方法是先将其 collect
到一个 Vec
中,然后对其进行迭代。
chunks
和 windows
返回子切片的原因;&[T]
中的元素数量不是类型的一部分,因此可以在运行时决定。
for slice in some_iter.windows(2)
。LinkedList
没有连续的存储空间Iterator::Item
的定义中没有可用的生命周期Vec<Iterator::Item>
作为分块迭代器的 Item
。struct Chunks<I: Iterator> {
elements: Vec<<I as Iterator>::Item>,
underlying: I,
}
impl<I: Iterator> Chunks<I> {
fn new(iterator: I, size: usize) -> Chunks<I> {
assert!(size > 0);
let mut result = Chunks {
underlying: iterator, elements: Vec::with_capacity(size)
};
result.refill(size);
result
}
fn refill(&mut self, size: usize) {
assert!(self.elements.is_empty());
for _ in 0..size {
match self.underlying.next() {
Some(item) => self.elements.push(item),
None => break,
}
}
}
}
impl<I: Iterator> Iterator for Chunks<I> {
type Item = Vec<<I as Iterator>::Item>;
fn next(&mut self) -> Option<Self::Item> {
if self.elements.is_empty() {
return None;
}
let new_elements = Vec::with_capacity(self.elements.len());
let result = std::mem::replace(&mut self.elements, new_elements);
self.refill(result.len());
Some(result)
}
}
fn main() {
let v = vec!(1, 2, 3, 4, 5);
for slice in Chunks::new(v.iter(), 2) {
println!("{:?}", slice);
}
}
将返回:
[1, 2]
[3, 4]
[5]
细心的读者会发现,我悄悄地从windows
转换到了chunks
。
windows
更难一些,因为它会多次返回相同的元素,需要将该元素Clone
。此外,由于每次需要返回完整的Vec
,因此它需要在内部保持一个Vec<Vec<Iterator::Item>>
。
这留给读者作为练习。
最后,关于性能的说明:所有这些分配都会对性能产生影响(特别是在windows
情况下)。
最佳的分配策略通常是分配一块单独的内存,然后依靠它来生存(除非量真的很大,否则需要流式传输)。
在Rust中,它被称为collect::<Vec<_>>()
。
由于Vec
具有chunks
和windows
方法(通过实现Deref<Target=[T]>
),因此您可以使用它们代替:
for slice in v.iter().collect::<Vec<_>>().chunks(2) {
println!("{:?}", slice);
}
for slice in v.iter().collect::<Vec<_>>().windows(2) {
println!("{:?}", slice);
}
for (prev, next) in some_iter.windows(2)
,其中 2 是运行时参数,我理解为我可以传递 3 并且有 for (n0, n1, n2) in some_iter.windows(3)
,但这是不可能的。您选择关注 (prev, next)
并忽略了运行时参数,这可能对 OP 来说没问题,但就我而言,这不是他们要求的(我不会读心术)。 - Matthieu M.windows
功能正是我所需要的,但我不确定如何实现它,因为我还是 Rust 的新手。有示例吗? - user5359531现在可以在夜间版本中使用名为array_chunks
的版本。
#![feature(iter_array_chunks)]
for [a, b, c] in some_iter.array_chunks() {
...
}
而且它能很好地处理余数:
#![feature(iter_array_chunks)]
for [a, b, c] in some_iter.by_ref().array_chunks() {
...
}
let rem = some_iter.into_remainder();
自Rust 1.51以来,使用常量泛型可以实现迭代器产生任意N
大小的常量数组[T; N]
。
我构建了两个独立的crate来实现这一点:
use iterchunks::IterChunks; // 0.2
for [a, b, c] in some_iter.array_chunks() {
...
}
use iterwindows::IterWindows; // 0.2
for [prev, next] in some_iter.array_windows() {
...
}
使用 Itertools
答案中给出的示例:
use iterchunks::IterChunks; // 0.2
fn main() {
let some_iter = vec![1, 2, 3, 4, 5, 6].into_iter();
for [prev, next] in some_iter.array_chunks() {
println!("{}--{}", prev, next);
}
}
1--2
3--4
5--6
大多数情况下,数组大小可以被推断出来,但您也可以明确指定它。此外,任何合理的大小 N
都可以使用,没有像 Itertools
的限制。
use iterwindows::IterWindows; // 0.2
fn main() {
let mut iter = vec![1, 2, 3, 4, 5, 6].into_iter().array_windows::<5>();
println!("{:?}", iter.next());
println!("{:?}", iter.next());
println!("{:?}", iter.next());
}
Some([1, 2, 3, 4, 5])
Some([2, 3, 4, 5, 6])
None
array_windows()
使用 clone 来多次生成元素,因此最适合用于引用和易于复制的类型。
iter_pairs
、iter_triples
,但目前在Rust中还没有通用的“任意大小元组”函数。 - Chris Emersonhttps://docs.rs/itertools/*/itertools/trait.Itertools.html#method.tuple_windows
。在发布答案之前,我想先查看它的代码。 - ideasman42