Подтвердить что ты не робот

Как выполнить итерацию по диапазону с помощью настраиваемого шага?

Как я могу перебирать диапазон в Rust с шагом, отличным от 1? Я исхожу из фона С++, поэтому я хотел бы сделать что-то вроде

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

В Rust мне нужно использовать функцию range, и, похоже, нет третьего аргумента, доступного для пользовательского шага. Как я могу это сделать?

4b9b3361

Ответ 1

range_step_inclusive и range_step, похоже, давно прошли. В ночное время существует нестабильный метод Iterator::step_by, который можно использовать следующим образом:

#![feature(iterator_step_by)]

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

Ответ 2

Мне кажется, что до тех пор, пока метод .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);
    }
}

Если требуется повторять несколько диапазонов разных типов, код можно сделать общим следующим образом:

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 сахарированием и будет продолжать работать, даже когда неустойчивые функции станут полезными; Кроме того, в отличие от подхода de-sugared, использующего стандартный Range s, он не теряет эффективности несколькими вызовами .next(). Недостатки в том, что для создания итератора требуется несколько строк кода, поэтому может стоить только для кода с большим количеством циклов.

Ответ 3

Вы напишете свой код на С++:

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

... в Rust вот так:

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

Я думаю, что версия Rust более читаема.

Ответ 4

Если вы шагнули на что-то предопределенное и небольшое, как 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();
    }
}