如何在 Rust 迭代器中跳过第 N 个元素?

18

迭代器有一个 skip 方法,可以跳过前 n 个元素:

let list = vec![1, 2, 3];
let iterator = list.iter();
let skip_iter = iterator.skip(2); //skip the first 2 elements

我找不到一种方法来跳过迭代器中仅为第n个元素。我是否需要自己实现一些东西,或者有一种我还没找到的方法?


1
我认为没有特定的方法来做到这一点,但是你可以使用 enumerate().filter(|(i, v)| (i + 1) != n).map(|(i, v)| v) 来跳过第 n 个元素。 - EvilTak
6个回答

23

这似乎是一个非常特定的操作。标准库或itertools crate中都没有适配器适用于此。

不过这很容易实现。可以枚举每个元素并过滤索引:

iter.enumerate().filter(|&(i, _)| i != n).map(|(_, v)| v)

Playground

:游乐场。

16

我偏爱使用 filter_map 版本。

fn main() {
    let v = vec![1, 2, 3];
    let n = 1;
    let x: Vec<_> = v.into_iter()
        .enumerate()
        .filter_map(|(i, e)| if i != n { Some(e) } else { None })
        .collect();
    println!("{:?}", x);
}

Playground

:游乐场。

这比.filter(..)有什么优势吗?它看起来更长,但没有任何收益。 - Centril
3
еЏҒдҢүз”Ёfilterзљ„и§Әе†іж–№жҰ€е°†иү”е›һеЊ…еђ«зөұеә•зљ„е…ѓз»„пәЊиЂЊиү™дёҒи§Әе†іж–№жҰ€е°†иү”е›һжІҰжњ‰зөұеә•зљ„еџғзҰЂе…ѓзө гЂ‚иү™еЏ–е†ідғҺж‚Ёжѓіи¦Ѓд»Ђд№€ж ·зљ„з»“жһњгЂ‚ - user25064
filter_map 闭包在 Rust 1.50 中可以更加简洁,如果你喜欢的话:|(i, e)| (i != n).then(|| e) - Orez

3
如果你有原始的收藏,那么它可能是“最初的回答”。
let items = ["a", "b", "c", "d"];
let skipped_2nd = items.iter().take(1).chain(items.iter().skip(2));

2

我已经想要跳过一些范围。在我看来,最好的方法是创建一个迭代器:

mod skip_range {
    use std::ops::Range;
    use std::iter::Skip;

    /// Either the user provided iterator, or a `Skip` one.
    enum Either<I: Iterator> {
        Iter(I),
        Skip(Skip<I>),
    }

    pub struct SkipRange<I: Iterator> {
        it: Option<Either<I>>,
        count: usize,
        range: Range<usize>,
    }

    impl<I: Iterator> SkipRange<I> {
        pub fn new(it: I, range: Range<usize>) -> Self {
            SkipRange { it: Some(Either::Iter(it)), count: 0, range }
        }
    }

    impl<I: Iterator> Iterator for SkipRange<I> {
        type Item = I::Item;

        fn next(&mut self) -> Option<Self::Item> {
            // If we are in the part we must skip, change the iterator to `Skip`
            if self.count == self.range.start {
                self.count = self.range.end;
                if let Some(Either::Iter(it)) = self.it.take() {
                    self.it = Some(Either::Skip(it.skip(self.range.end - self.range.start)));
                }
            } else {
                self.count += 1;
            }
            match &mut self.it {
                Some(Either::Iter(it)) => it.next(),
                Some(Either::Skip(it)) => it.next(),
                _ => unreachable!(),
            }
        }
    }
}

use skip_range::SkipRange;

fn main() {
    let v = vec![0, 1, 2, 3, 4, 5];
    let it = SkipRange::new(v.into_iter(), 2..4);

    let res: Vec<_> = it.collect();
    assert_eq!(res, vec![0, 1, 4, 5]);
}

原则是使用两个不同的迭代器:第一个由用户提供,第二个是从第一个创建的 Skip 迭代器。

0

只有适用于向量的情况下,for_each() 才不会出错。 - Stargateur
我认为那并不是严格正确的,它应该适用于各种类型,如 Index<RangeTo<usize>> + Index<RangeFrom<usize>>,对吧?例如,如果我将 v 更改为 [&'static str; 4]&[&'static str; 4] 而不是 Vec,它似乎可以正常工作。编辑:错别字。 - n8henrie
另外,我从未听说过其他人对for_each提出抱怨。那只是你的观点,还是在其他地方被正式诋毁了? - n8henrie
向量本质上是一个切片。对于你最后的消息,无论是我的观点还是被接受的使用for_each()都不太好,因为迭代器不应该有副作用(或者至少要避免)。当Rust中有明确格式化的for循环时,没有理由优先选择for_each()。 - Stargateur
正确。此外,Vec 是 OP 在他们的示例中使用的内容,由于所讨论的用例需要一个序列(应该保持顺序以使跳过有意义),我不明白为什么你对我在我的答案中使用它有异议。https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.for_each 特别提到 chainfor_each 可能具有优势的情况之一,因此似乎可能有某些理由更喜欢它。对吗?编辑:错别字 - n8henrie
我想知道这个优化是否有效,你明白我的意思,但问题仍然在于迭代器,向量只是为了提供一个简单的迭代器示例。 - Stargateur

0
更简洁:
let iter = vs
            .iter()
            .enumerate()
            .filter_map(|(i, el)| (i == n).then(|| el));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接