跳过第n个元素的迭代器

  • 本文关键字:元素 迭代器 rust
  • 更新时间 :
  • 英文 :


比起从迭代器中获取每个第n个元素(我可以使用Iterator::step_by),我想跳过每个第n个元素。我怎样才能做到这一点呢?有没有标准库或者itertools函数?

这是我想出的每隔7次说一次的话。它需要enumerate,filtermap,尽管可以使用filter_map代替后两个。

(0..100).enumerate()
.filter(|&(i, x)| (i + 1) % 7 != 0)
.map(|(i, x)| x);

如何将其转换为函数,以便我可以简单地写:

(0..100).skip_every(7)

如果您想获得您所要求的确切接口,此时最好的选择是实现自定义迭代器适配器类型。下面是这种类型的基本版本:

pub struct SkipEvery<I> {
inner: I,
every: usize,
index: usize,
}
impl<I> SkipEvery<I> {
fn new(inner: I, every: usize) -> Self {
assert!(every > 1);
let index = 0;
Self {
inner,
every,
index,
}
}
}
impl<I: Iterator> Iterator for SkipEvery<I> {
type Item = I::Item;
fn next(&mut self) -> Option<Self::Item> {
if self.index == self.every - 1 {
self.index = 1;
self.inner.nth(1)
} else {
self.index += 1;
self.inner.next()
}
}
}
pub trait IteratorSkipEveryExt: Iterator + Sized {
fn skip_every(self, every: usize) -> SkipEvery<Self> {
SkipEvery::new(self, every)
}
}
impl<I: Iterator + Sized> IteratorSkipEveryExt for I {}

(游乐场)

一个更完整的实现还可以添加Iterator方法的优化版本,以及DoubleEndedIteratorExactSizeIterator的实现——参见StepBy的实现作为一个例子。

你的代码很容易变成一个函数:

fn skip_every<I: Iterator> (iter: I, n: usize) -> impl Iterator<Item = <I as Iterator>::Item> {
iter.enumerate()
.filter_map(move |(i, v)| if (i + 1) % n != 0 { Some (v) } else { None })
}
fn main() {
println!("{:?}", skip_every (0..20, 7).collect::<Vec<_>>());
}

游乐场

或者避免昂贵的模:

fn skip_every2<I: Iterator> (iter: I, n: usize) -> impl Iterator<Item = <I as Iterator>::Item> {
iter.zip ((0..n).rev().cycle()).filter_map (|(v, i)| if i != 0 { Some (v) } else { None })
}

游乐场

最新更新