Jak iterować zakres z krokiem niestandardowym?

106

Jak mogę iterować zakres w Rust z krokiem innym niż 1? Pochodzę z C ++, więc chciałbym zrobić coś takiego

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

W Rust muszę użyć tej rangefunkcji i nie wygląda na to, że jest dostępny trzeci argument za niestandardowym krokiem. Jak mogę to osiągnąć?

Fruktoza syntaktyczna
źródło

Odpowiedzi:

13

Wydaje mi się, że dopóki .step_bymetoda nie zostanie ustabilizowana, można łatwo osiągnąć to, co chcesz, za pomocą Iterator(co Rangei tak jest tak naprawdę):

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

Jeśli trzeba iterować wiele zakresów różnych typów, kod można uczynić generycznym w następujący sposób:

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

Zostawię to tobie, aby wyeliminować sprawdzanie górnych granic, aby utworzyć otwartą strukturę, jeśli wymagana jest nieskończona pętla ...

Zaletą tego podejścia jest to, że działa z forcukrowaniem i będzie działać nawet wtedy, gdy niestabilne funkcje staną się użyteczne; ponadto, w przeciwieństwie do metody pozbawionej cukru, używającej standardowych Range, nie traci wydajności przez wielokrotne .next()wywołania. Wadą jest to, że skonfigurowanie iteratora zajmuje kilka wierszy kodu, więc może być tego warte tylko w przypadku kodu, który ma dużo pętli.

GordonB Dobrze
źródło
Dodając inny typ, Udo drugiej opcji możesz użyć typów, które obsługują dodawanie z innym typem i nadal dają plik T. Na przykład przychodzą na myśl czas i trwanie.
Ryan
@Ryan, to wydaje się być dobrym pomysłem i powinno działać, ze strukturą zdefiniowaną w następujący sposób: struct StepRange <T> (T, T, U) gdzie dla <'a,' b> & 'a T: Add <&' b U, Output = T>, T: PartialOrd, T: Clone; co powinno pozwolić na różne okresy istnienia dla typów wejściowych T i U.
GordonBGood
4

Napisałbyś swój kod w C ++:

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

... w Rust tak:

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

Myślę, że wersja Rusta też jest bardziej czytelna.

kmky
źródło
Re: wstawiając "continue" w pętli, można by to zrobić tylko wewnątrz gałęzi warunkowej, nawet w strukturze for. Jeśli tak, to myślę, że byłoby w porządku, gdyby inkrementować wewnątrz gałęzi warunkowej w strukturze while przed "kontynuowaniem" -ing, i wtedy działałoby to zgodnie z zamierzeniami. A może coś przeoczę?
WDS
1
@WDS to sprzeczna z intuicją praca nad uzyskaniem podstawowej funkcji języka continue, aby działała poprawnie. Chociaż można to zrobić, ten projekt zachęca do błędów.
Chai T. Rex
2

Jeśli przechodzisz przez coś wstępnie zdefiniowanego i małego, jak 2, możesz chcieć użyć iteratora do ręcznego przechodzenia. na przykład:

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

Możesz nawet użyć tego, aby przejść o dowolną kwotę (chociaż jest to zdecydowanie dłuższe i trudniejsze do strawienia):

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();
    }
}
Leigh McCulloch
źródło