一次迭代多个变量,重叠(slice::windows
)或不重复()可能很有用slice::chunks
.
这仅适用于切片; 是否可以为迭代器执行此操作,为方便起见使用元组?
可以写下以下内容:
for (prev, next) in some_iter.windows(2) {
...
}
Run Code Online (Sandbox Code Playgroud)
如果没有,它是否可以作为现有迭代器的特征实现?
Ros*_*hur 20
从 Rust 1.51 开始,这可以通过const 泛型实现,其中迭代器[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();
Run Code Online (Sandbox Code Playgroud)
use itermore::IterArrayWindows; // 0.7
for [prev, next] in some_iter.array_windows() {
...
}
Run Code Online (Sandbox Code Playgroud)
使用答案中给出的示例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);
}
}
Run Code Online (Sandbox Code Playgroud)
这输出
1--2
3--4
5--6
Run Code Online (Sandbox Code Playgroud)
大多数情况下,可以推断数组大小,但您也可以明确指定它。另外,N
可以使用任何合理的尺寸,没有像本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);
}
}
Run Code Online (Sandbox Code Playgroud)
这输出
Some([1, 2, 3, 4, 5])
Some([2, 3, 4, 5, 6])
None
Run Code Online (Sandbox Code Playgroud)
注意:array_windows()
使用克隆多次生成元素,因此它最好用于引用并且复制类型成本低廉。
chunks 版本现已在 nightly 上以该名称提供array_chunks
#![feature(iter_array_chunks)]
for [a, b, c] in some_iter.array_chunks() {
...
}
Run Code Online (Sandbox Code Playgroud)
它可以很好地处理余数:
#![feature(iter_array_chunks)]
for [a, b, c] in some_iter.by_ref().array_chunks() {
...
}
let rem = some_iter.into_remainder();
Run Code Online (Sandbox Code Playgroud)
如果您不迭代某些连续的集合,则迭代器版本特别有用。但是,根据您的用例,您可能会发现收集到第一个Vec
并使用切片方法可能会更快,甚至包括将迭代器分配到Vec
. array_windows
在需要克隆元素的情况下尤其如此。
Mat*_* M. 14
TL; DR:拥有chunks
和windows
使用任意迭代器/集合的最佳方法是首先将collect
其转换为a Vec
并迭代它.
在Rust中,所要求的确切语法是不可能的.
问题是在Rust中,函数的签名取决于类型,而不是值,并且存在Dependent Typing时,实现它的语言很少(很难).
这就是为什么chunks
并windows
顺便返回一个子切片; a中元素的数量&[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);
}
}
Run Code Online (Sandbox Code Playgroud)
将返回:
Run Code Online (Sandbox Code Playgroud)[1, 2] [3, 4] [5]
精明的读者会意识到我偷偷地从切换windows
到了chunks
.
windows
更难,因为它多次返回相同的元素,这需要元素Clone
.此外,由于Vec
每次都需要返回一个完整的内容,因此需要在内部保留一个Vec<Vec<Iterator::Item>>
.
这留给读者练习.
最后,关于绩效的说明:所有这些分配都会受到伤害(特别是在这种windows
情况下).
最好的分配策略通常是分配一块内存然后实现(除非数量非常大,在这种情况下需要流式传输).
它collect::<Vec<_>>()
在Rust中被称为.
由于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);
}
Run Code Online (Sandbox Code Playgroud)
有时最好的解决方案是最简单的.
She*_*ter 13
使用Itertools::tuples
最多四元组的迭代器块是可能的:
extern crate itertools;
use itertools::Itertools;
fn main() {
let some_iter = vec![1, 2, 3, 4, 5, 6].into_iter();
for (prev, next) in some_iter.tuples() {
println!("{}--{}", prev, next);
}
}
Run Code Online (Sandbox Code Playgroud)
(游乐场)
1--2
3--4
5--6
Run Code Online (Sandbox Code Playgroud)
以及最多4个元组窗口Itertools::tuple_windows
:
extern crate itertools;
use itertools::Itertools;
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);
}
}
Run Code Online (Sandbox Code Playgroud)
(游乐场)
1--2
2--3
3--4
4--5
5--6
Run Code Online (Sandbox Code Playgroud)