循环遍历 Rust 迭代器指定次数

10
如何有限次循环迭代器?
我期望像这样的输出结果是1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3,然后停止:
vec![1, 2, 3].iter().cycle(4)
//                         ^ but .cycle() doesn't take an argument...

我一开始不知道迭代器的长度。


2
这将是itertools的一个不错的补充。 - Boiethios
3个回答

14

有一个简单的方法是重复迭代器本身,取前4个并将它们展开:

一种简单的方法是使用迭代器本身进行重复,然后取前四个元素并将其打平:

fn main() {
    let v = vec![1, 2, 3];
    let res = std::iter::repeat(v.iter())
        .take(4)
        .flatten()
        .collect::<Vec<_>>();
    dbg!(res);
}

使用此处代码进行微型基准测试,比较3种不同的方法:
  • 重复-取-展平(本答案中)
  • 手写循环
  • 模仿Iterator::cyclecycle_n 实现。
感谢rustc,在输入足够大时,cycle_n始终表现优异,而在小输入情况下,repeat-take-flatten表现最佳。 Micro-benchmark between repeat-take-flatten, hand-rolled loop and <code>cycle_n</code>

1
@FrenchBoiethios godbolt链接,不确定是否经过良好优化:D - edwardw
@edwardw 相比手写循环:https://godbolt.org/z/GRbTt7 - phimuemue
1
@phimuemue 几百行汇编对于像我这样的凡人来说仍然太难解读了,哈哈。做了一些微基准测试。 - edwardw

4

标准库中没有这样一个迭代器。

如果您知道迭代器的大小,可以将您的数字乘以迭代器的大小后再take

fn cycle_n_times<T: Clone>(slice: &[T], count: usize) -> impl Iterator<Item = &T> {
    slice.iter().cycle().take(slice.len() * count)
}

或者您可以编写更通用的自定义代码:
pub struct Ncycles<I> {
    orig: I,
    iter: I,
    count: usize,
}

impl<I: Clone> Ncycles<I> {
    fn new(iter: I, count: usize) -> Ncycles<I> {
        Ncycles {
            orig: iter.clone(),
            iter,
            count,
        }
    }
}

impl<I> Iterator for Ncycles<I>
where
    I: Clone + Iterator,
{
    type Item = <I as Iterator>::Item;

    #[inline]
    fn next(&mut self) -> Option<<I as Iterator>::Item> {
        match self.iter.next() {
            None if self.count == 0 => None,
            None => {
                self.iter = self.orig.clone();
                self.count -= 1;
                self.iter.next()
            }
            y => y,
        }
    }
}

#[test]
fn it_work() {
    Ncycles::new(vec![1, 2, 3].iter(), 4).eq(&[1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]);
}

1
首先将元素收集到一个切片中似乎是合理的,因为我们需要多次使用这些元素。此外,当您的迭代器不是 Clone 时(例如文件行的迭代器),这也是唯一可行的解决方案。 - Sven Marnach
@SvenMarnach 如果我必须在这种情况下优化代码,我会进行基准测试。 - Boiethios

0

可以利用slice::repeat,但我不能想象这非常有效:

let v = vec![1, 2, 3];
let it = v.iter();
println!("{:?}", it.map(|x| *x).collect::<Vec<i32>>().repeat(4).iter());

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