是否可以在不创建特殊迭代器的情况下每次迭代执行不同的量?

时间:2017-05-06 16:53:48

标签: loops rust flow-control

在C中,for循环有一个可选的增量部分,我有时会在Rust中错过:

for (uint i = 0; i < max; i = step_function(i, j, k)) {
    /* many lines of code! */
}

这可以用Rust编写:

let mut i: u32 = 0;
while (i < max) {
    // 
    // many lines of code! 
    //
    i = step_function(i, j, k);
}

...但是如果{em>&#34;多行代码&#34; 中存在continue,这将引入错误。我个人的偏好也是将增量保持在循环的顶部。

如果没有创建一个特殊的迭代器来处理这个问题,是否有一种方法可以更紧密地匹配C样式,同时解决上述两个问题?

通过&#34;特殊迭代器&#34;,我的意思是不必在for循环之外定义迭代器类型和方法。

虽然看起来像是一个人为的要求,但必须为一次使用定义一个迭代器 - 在读取和编写代码时会增加一些开销。

尽管@ kennytm的答案显示了可重用的StepByFn迭代器如何工作,但使用闭包会为代码增加一些限制,否则就不会存在。

1 个答案:

答案 0 :(得分:4)

如果您可以导入外部包,则应使用itertools::iterate

extern crate itertools;
use itertools::iterate;

fn main() {
    for i in iterate(0, |i| 2*i + 3).take_while(|i| *i < 100) {
        println!("{}", i);
        // 0 3 9 21 45 93
    }
}

如果你真的错过了C-style for循环,你可以使用cfor crate:

#[macro_use] extern crate cfor;

fn main() {
    cfor!{ let mut i = 0; i < 100; i = 2*i + 3; {
        println!("{}", i);
        // 0 3 9 21 45 93
    }}
}

如果您只限制使用标准库,那么创建一个特殊的迭代器将是最惯用的方式。

fn main() {
    for i in StepByFn::new(0, 100, |i| 2*i + 3) {
        println!("{}", i);
        // 0 3 9 21 45 93
    }
}

struct StepByFn<T, F> {
    begin: T,
    end: T,
    step: F,
}

impl<T, F: FnMut(&T) -> T> StepByFn<T, F> {
    pub fn new(begin: T, end: T, step: F) -> StepByFn<T, F> {
        StepByFn { begin, end, step }
    }
}

impl<T: PartialOrd, F: FnMut(&T) -> T> Iterator for StepByFn<T, F> {
    type Item = T;
    fn next(&mut self) -> Option<T> {
        if self.begin >= self.end {
            return None;
        }
        let next = (self.step)(&self.begin);
        let prev = std::mem::replace(&mut self.begin, next);
        Some(prev)
    }
}

也可以使用repeat().scan()创建一个内联迭代器,但它非常难看并且不能很好地表达意图

use std::iter::repeat;

fn main() {
    for i in repeat(()).scan(0, |i, ()| { 
        let old = *i; 
        *i = 2*old + 3; 
        if old < 100 { Some(old) } else { None } 
    }) {
        println!("{}", i);
        // 0 3 9 21 45 93
    }
}