Bagaimana cara mengulang rentang dengan langkah kustom?

100

Bagaimana saya bisa mengulang rentang di Rust dengan langkah selain 1? Saya berasal dari latar belakang C ++ jadi saya ingin melakukan sesuatu seperti

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

Di Rust saya perlu menggunakan rangefungsi, dan sepertinya tidak ada argumen ketiga yang tersedia untuk memiliki langkah kustom. Bagaimana saya bisa melakukannya?

Fruktosa sintaksis
sumber

Jawaban:

12

Tampak bagi saya bahwa sampai .step_bymetode dibuat stabil, seseorang dapat dengan mudah mencapai apa yang Anda inginkan dengan Iterator(yang sebenarnya adalah apa 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);
    }
}

Jika seseorang perlu mengulang beberapa rentang dari tipe yang berbeda, kode dapat dibuat umum sebagai berikut:

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

Saya akan menyerahkan kepada Anda untuk menghilangkan pemeriksaan batas atas untuk membuat struktur ujung terbuka jika diperlukan loop tak terbatas ...

Keuntungan dari pendekatan ini adalah berfungsi dengan forsugaring dan akan terus berfungsi bahkan ketika fitur yang tidak stabil menjadi dapat digunakan; juga, tidak seperti pendekatan de-sugared yang menggunakan standar Range, ia tidak kehilangan efisiensi dengan beberapa .next()panggilan. Kekurangannya adalah dibutuhkan beberapa baris kode untuk mengatur iterator sehingga mungkin hanya sepadan untuk kode yang memiliki banyak loop.

GordonBBagus
sumber
Dengan menambahkan tipe lain, Uke opsi kedua Anda bisa menggunakan tipe yang mendukung penambahan dengan tipe berbeda dan tetap menghasilkan T. Misalnya waktu dan durasi muncul dalam pikiran.
Ryan
@Ryan, itu sepertinya ide yang bagus dan seharusnya berfungsi, dengan struct yang didefinisikan sebagai berikut: struct StepRange <T> (T, T, U) dimana untuk <'a,' b> & 'a T: Add <&' b U, Output = T>, T: PartialOrd, T: Clone; yang memungkinkan masa pakai yang berbeda untuk jenis input T dan U.
GordonBGood
3

Anda akan menulis kode C ++ Anda:

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

... di Rust seperti ini:

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

Saya rasa versi Rust juga lebih mudah dibaca.

kmky
sumber
Re: menyisipkan "lanjutkan" dalam loop, seseorang hanya akan melakukan ini di dalam cabang bersyarat bahkan dalam struktur for, saya pikir. Jika demikian, maka saya pikir tidak apa-apa untuk menaikkan di dalam cabang bersyarat dalam struktur while sebelum "lanjutkan" -ing, dan itu akan berfungsi sebagaimana mestinya. Atau apakah saya mengabaikan sesuatu?
WDS
1
@WDS itulah kesibukan yang berlawanan dengan intuisi untuk mendapatkan fitur dasar bahasa continue,, agar berfungsi dengan baik. Meski bisa dilakukan, desain ini mendorong bug.
Chai T.Rex
2

Jika Anda melangkah dengan sesuatu yang telah ditentukan sebelumnya, dan kecil seperti 2, Anda mungkin ingin menggunakan iterator untuk melangkah secara manual. misalnya:

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

Anda bahkan dapat menggunakan ini untuk melangkah dengan jumlah yang berubah-ubah (meskipun ini pasti semakin lama dan sulit untuk dicerna):

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
sumber