Як зробити ітерацію по діапазону за допомогою користувацького кроку?


100

Як я можу здійснити ітерацію по діапазону в Rust з кроком, відмінним від 1? Я походжу з фону C ++, тому я хотів би зробити щось подібне

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

У Rust мені потрібно використовувати rangeфункцію, і, схоже, не існує третього аргументу для наявності спеціального кроку. Як я можу це зробити?

Відповіді:


136

range_step_inclusiveі range_stepїх давно немає.

Станом на Rust 1.28 Iterator::step_byстабільний:

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


Зауважте, що цей метод не підтримує 64-бітний крок на машинах із 32-бітовим типом розміру.
user202729

12

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


Додавши інший тип, Uдо другого варіанту ви можете використовувати типи, які підтримують додавання з іншим типом і все одно дають a T. Наприклад, час і тривалість спадають на думку.
Райан

@Ryan, це здається гарною ідеєю і має працювати, при цьому структура визначається наступним чином: struct StepRange <T> (T, T, U) де для <'a,' b> & 'a T: Add <&' b U, вихід = T>, T: PartialOrd, T: Clone; що має забезпечувати різні терміни життя для вхідних типів T та U.
GordonBGood


3

Ви б написали свій код на C ++:

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

... в Rust так:

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

Я думаю, що версія Rust теж читабельніша.


Re: вставляючи "continue" у цикл, я б міг зробити це всередині умовної гілки навіть у структурі for, я думаю. Якщо так, тоді я думаю, що було б нормально збільшувати всередині умовної гілки в структурі while перед "continue" -ing, і тоді вона працювала б за призначенням. Або я щось пропускаю?
WDS

1
@WDS, який не має інтуїтивної напруженої роботи, щоб отримати базову характеристику мови continue, для належної роботи. Хоча це можна зробити, цей дизайн заохочує помилок.
Chai T. Rex

2

Якщо ви переходите до чогось заздалегідь визначеного і невеликого, як 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();
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.