slice :: chunks/windows是否有等效的迭代器来循环对,三元组等?

ide*_*n42 15 iterator rust

一次迭代多个变量,重叠(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:拥有chunkswindows使用任意迭代器/集合的最佳方法是首先将collect其转换为a Vec并迭代.


在Rust中,所要求的确切语法是不可能的.

问题是在Rust中,函数的签名取决于类型,而不是,并且存在Dependent Typing时,实现它的语言很少(很难).

这就是为什么chunkswindows顺便返回一个子切片; 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)

将返回:

[1, 2]
[3, 4]
[5]
Run Code Online (Sandbox Code Playgroud)

精明的读者会意识到我偷偷地从切换windows到了chunks.

windows更难,因为它多次返回相同的元素,这需要元素Clone.此外,由于Vec每次都需要返回一个完整的内容,因此需要在内部保留一个Vec<Vec<Iterator::Item>>.

这留给读者练习.


最后,关于绩效的说明:所有这些分配都会受到伤害(特别是在这种windows情况下).

最好的分配策略通常是分配一块内存然后实现(除非数量非常大,在这种情况下需要流式传输).

collect::<Vec<_>>()在Rust中被称为.

由于Vec有一个chunkswindows方法(由于实现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)

有时最好的解决方案是最简单的.

  • 很抱歉投反对票,但是*所要求的确切语法在 Rust 中是不可能的*是不正确的;请检查[我的答案](http://stackoverflow.com/a/42139758/155423)。不过,您的其余大部分分析都是有道理的。 (4认同)
  • @Shepmaster:您的答案也没有要求的确切语法。请求是 some_iter.windows(2) 中的 `for (prev, next)`,带有 2 个运行时参数,我将其解释为我可以传递 3 并且在 some_iter.windows 中有 `for (n0, n1, n2) (3)` 那是不可能的。您选择关注 `(prev, next)` 并忽略运行时参数,OP 可能没问题,但就我而言,这不是他们所要求的(我不读心)。 (2认同)

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)