【问题标题】:How do I iterate over a range with a custom step?如何使用自定义步骤迭代范围?
【发布时间】:2015-03-09 16:50:45
【问题描述】:

如何在 Rust 中以 1 以外的步骤迭代一个范围?我来自 C++ 背景,所以我想做类似的事情

for(auto i = 0; i <= n; i+=2) {
    //...
}

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

【问题讨论】:

    标签: rust


    【解决方案1】:

    range_step_inclusiverange_step 早已不复存在。

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

    fn main() {
        for x in (1..10).step_by(2) {
            println!("{}", x);
        }
    }
    

    【讨论】:

    【解决方案2】:

    在我看来,在.step_by 方法稳定之前,使用Iterator 可以轻松完成您想要的(无论如何Ranges 确实如此):

    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);
        }
    }
    

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

    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);
        }
    }
    

    如果需要无限循环,我将由您来消除上限检查以创建开放式结构...

    这种方法的优点是适用于for 加糖,即使在不稳定的功能变得可用时也能继续工作;此外,与使用标准Ranges 的脱糖方法不同,它不会因多次.next() 调用而降低效率。缺点是需要几行代码来设置迭代器,所以可能只对有很多循环的代码才值得。

    【讨论】:

    • 通过将另一个类型 U 添加到您的第二个选项中,您可以使用支持添加不同类型的类型,并且仍然产生 T。例如时间和持续时间。
    • @Ryan,这似乎是个好主意,应该可以工作,结构定义如下: struct StepRange(T, T, U) where for & 'a T: Add, T: PartialOrd, T: Clone;这应该允许输入 T 和 U 类型的不同生命周期。
    【解决方案3】:

    你会编写你的 C++ 代码:

    for (auto i = 0; i <= n; i += 2) {
        //...
    }
    

    ...在 Rust 中是这样的:

    let mut i = 0;
    while i <= n {
        // ...
        i += 2;
    }
    

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

    【讨论】:

    • Re:在循环中插入“继续”,我认为即使在 for 结构中也只能在条件分支内执行此操作。如果是这样,那么我认为可以在“继续”之前在 while 结构中的条件分支内递增,然后它将按预期工作。还是我忽略了什么?
    • @WDS 让语言的基本功能continue 正常工作是违反直觉的忙碌工作。虽然可以做到,但这种设计鼓励了错误。
    【解决方案4】:

    numrange_step 一起使用

    【讨论】:

      【解决方案5】:

      如果您正在按预定义的内容进行步进,并且像 2 一样小,您可能希望使用迭代器手动步进。例如:

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

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

      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();
          }
      }
      

      【讨论】:

      • 为什么在forstep_by 上使用这个?
      猜你喜欢
      • 2012-11-05
      • 2015-10-01
      • 2013-03-20
      • 2021-10-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多