Làm cách nào để lặp lại phạm vi với một bước tùy chỉnh?


100

Làm cách nào để tôi có thể lặp lại một phạm vi trong Rust với một bước khác với 1? Tôi đến từ nền tảng C ++ nên tôi muốn làm điều gì đó như

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

Trong Rust, tôi cần sử dụng rangehàm và có vẻ như không có sẵn đối số thứ ba để có một bước tùy chỉnh. Làm thế nào tôi có thể thực hiện điều này?

Câu trả lời:



12

Đối với tôi, dường như cho đến khi .step_byphương thức này được ổn định, người ta có thể dễ dàng thực hiện những gì bạn muốn với một Iterator( Rangedù sao thì đó cũng là điều thực sự):

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

Nếu một người cần lặp lại nhiều phạm vi thuộc các loại khác nhau, mã có thể được tạo chung như sau:

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

Tôi sẽ giao nó cho bạn để loại bỏ kiểm tra giới hạn trên để tạo cấu trúc kết thúc mở nếu cần một vòng lặp vô hạn ...

Ưu điểm của phương pháp này là hoạt động với forđường và sẽ tiếp tục hoạt động ngay cả khi các tính năng không ổn định có thể sử dụng được; Ngoài ra, không giống như cách tiếp cận loại bỏ đường bằng cách sử dụng tiêu chuẩn Ranges, nó không làm mất hiệu quả bởi nhiều .next()cuộc gọi. Nhược điểm là phải mất một vài dòng mã để thiết lập trình lặp, vì vậy có thể chỉ đáng giá đối với mã có nhiều vòng lặp.


Bằng cách thêm một loại khác, Uvào tùy chọn thứ hai của bạn, bạn có thể sử dụng các loại hỗ trợ bổ sung với một loại khác và vẫn mang lại a T. Ví dụ thời gian và thời lượng xuất hiện trong tâm trí.
Ryan

@Ryan, đó có vẻ là một ý tưởng hay và sẽ hiệu quả, với cấu trúc được định nghĩa như sau: struct StepRange <T> (T, T, U) where for <'a,' b> & 'a T: Add <&' b U, Output = T>, T: PartialOrd, T: Clone; điều này sẽ cho phép các vòng đời khác nhau cho các loại đầu vào T và U.
GordonBGood


3

Bạn sẽ viết mã C ++ của mình:

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

... trong Rust như vậy:

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

Tôi nghĩ rằng phiên bản Rust cũng dễ đọc hơn.


Re: chèn "continue" trong vòng lặp, người ta sẽ chỉ làm điều này bên trong một nhánh có điều kiện ngay cả trong cấu trúc for, tôi nghĩ vậy. Nếu đúng như vậy, thì tôi nghĩ sẽ ổn nếu tăng bên trong nhánh điều kiện trong cấu trúc while trước khi "tiếp tục" -ing, và sau đó nó sẽ hoạt động như dự định. Hay tôi đang bỏ qua thứ gì đó?
WDS

1
@WDS đó là công việc bận rộn phản trực giác để có được một tính năng cơ bản của ngôn ngữ, continuehoạt động bình thường. Mặc dù nó có thể được thực hiện, thiết kế này khuyến khích các lỗi.
Chai T. Rex

2

Nếu bạn đang bước theo một cái gì đó được xác định trước và nhỏ như 2, bạn có thể muốn sử dụng trình lặp để bước theo cách thủ công. ví dụ:

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

Bạn thậm chí có thể sử dụng điều này để từng bước một số lượng tùy ý (mặc dù điều này chắc chắn ngày càng dài và khó tiêu hóa):

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();
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.