如何使用自定义步骤迭代范围?

Syn*_*ose 55 rust

如何使用1以外的步骤迭代Rust中的范围?我来自C++背景,所以我想做点什么

for(auto i = 0; i <= n; i+=2) {
    //...
}
Run Code Online (Sandbox Code Playgroud)

在Rust中我需要使用该range函数,并且似乎没有第三个参数可用于自定义步骤.我怎么能做到这一点?

Pil*_*ill 72

range_step_inclusive并且range_step早已不复存在.

从Rust 1.28开始,Iterator::step_by是稳定的:

fn main() {
    for x in (1..10).step_by(2) {
        println!("{}", x);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 不幸的是,这并不能很好地处理负面步骤。如果您想要执行“(11..=1).step_by(-2)”(从 11 到 1(含)的奇数),则必须执行“(1..12)”。 step_by(2).rev()`,这不太直观。 (5认同)

Gor*_*ood 10

在我看来,在.step_by方法稳定之前,人们可以轻松地完成你想要的东西Iterator(Range无论如何真正是这样):

struct SimpleStepRange(isize, isize, isize);  // start, end, and step

impl Iterator for SimpleStepRange {
    type Item = isize;

    #[inline]
    fn next(&mut self) -> Option<isize> {
        if self.0 < self.1 {
            let v = self.0;
            self.0 = v + self.2;
            Some(v)
        } else {
            None
        }
    }
}

fn main() {
    for i in SimpleStepRange(0, 10, 2) {
        println!("{}", i);
    }
}
Run Code Online (Sandbox Code Playgroud)

如果需要迭代不同类型的多个范围,则可以使代码通用如下:

use std::ops::Add;

struct StepRange<T>(T, T, T)
    where for<'a> &'a T: Add<&'a T, Output = T>,
          T: PartialOrd,
          T: Clone;

impl<T> Iterator for StepRange<T>
    where for<'a> &'a T: Add<&'a T, Output = T>,
          T: PartialOrd,
          T: Clone
{
    type Item = T;

    #[inline]
    fn next(&mut self) -> Option<T> {
        if self.0 < self.1 {
            let v = self.0.clone();
            self.0 = &v + &self.2;
            Some(v)
        } else {
            None
        }
    }
}

fn main() {
    for i in StepRange(0u64, 10u64, 2u64) {
        println!("{}", i);
    }
}
Run Code Online (Sandbox Code Playgroud)

如果需要无限循环,我会留给你消除上界检查以创建一个开放式结构...

这种方法的优点是可以for加糖,并且即使在不稳定的特征变得可用时也会继续工作; 此外,与使用标准Ranges 的去糖方法不同,它不会因多次.next()调用而失去效率.缺点是需要几行代码才能设置迭代器,因此对于具有大量循环的代码来说可能是值得的.


kmk*_*mky 6

您将编写 C++ 代码:

for (auto i = 0; i <= n; i += 2) {
    //...
}
Run Code Online (Sandbox Code Playgroud)

...在 Rust 中就像这样:

let mut i = 0;
while i <= n {
    // ...
    i += 2;
}
Run Code Online (Sandbox Code Playgroud)

我认为 Rust 版本也更具可读性。


Lei*_*och 5

如果您要单步执行预定义的值,并且像 2 这样小,您可能希望使用迭代器手动单步执行。例如:

let mut iter = 1..10;
loop {
    match iter.next() {
        Some(x) => {
            println!("{}", x);
        },
        None => break,
    }
    iter.next();
}
Run Code Online (Sandbox Code Playgroud)

您甚至可以使用它来步进任意数量(尽管这肯定会变得越来越长且难以消化):

let mut iter = 1..10;
let step = 4;
loop {
    match iter.next() {
        Some(x) => {
            println!("{}", x);
        },
        None => break,
    }
    for _ in 0..step-1 {
        iter.next();
    }
}
Run Code Online (Sandbox Code Playgroud)