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
如果需要获取部分块/窗口,可以获取
TL;DR:在任意迭代器/集合上拥有
chunks
和 windows
的最佳方法是首先将 collect
放入 Vec
中,然后迭代 that。
所要求的确切语法在 Rust 中是不可能的。
问题在于,在 Rust 中,函数的签名取决于 types,而不是 values,虽然存在依赖类型,但实现它的语言很少(很难)。
这就是为什么
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);
}
有时最好的解决方案是最简单的。
自 Rust 1.51 起,这可以通过 const generics 实现,其中迭代器为任何 [T; N]
生成恒定大小的数组
N
。
itermore
,在单独的扩展特征下提供 array_chunks()
和 array_windows()
方法。
use itermore::IterArrayChunks; // 0.7
for [a, b, c] in some_iter.by_ref().array_chunks() {
...
}
let rem = some_iter.into_remainder();
use itermore::IterArrayWindows; // 0.7
for [prev, next] in some_iter.array_windows() {
...
}
使用
Itertools
答案中给出的示例:
use itermore::IterArrayChunks; // 0.7
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 itermore::IterArrayWindows; // 0.7
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()
使用克隆多次生成元素,因此它最好用于引用并且复制类型成本低廉。
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();
如果您不迭代某些连续的集合,则迭代器版本特别有用。但是,根据您的用例,您可能会发现首先收集到
Vec
中并使用切片方法可能会更快,甚至包括将迭代器分配到 Vec
中的时间。对于需要克隆元素的 array_windows
情况尤其如此。