ฉันจะทำซ้ำในช่วงด้วยขั้นตอนที่กำหนดเองได้อย่างไร


100

ฉันจะทำซ้ำในช่วงใน Rust ด้วยขั้นตอนอื่นที่ไม่ใช่ 1 ได้อย่างไร ฉันมาจากพื้นหลัง C ++ ดังนั้นฉันต้องการทำสิ่งที่ชอบ

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

ใน Rust ฉันจำเป็นต้องใช้rangeฟังก์ชันนี้และดูเหมือนว่าจะไม่มีอาร์กิวเมนต์ที่สามสำหรับการมีขั้นตอนที่กำหนดเอง ฉันจะทำสิ่งนี้ให้สำเร็จได้อย่างไร?

คำตอบ:


136

range_step_inclusiveและrange_stepหายไปนาน

ณ สนิม 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น้ำตาลและจะยังคงทำงานต่อไปแม้ว่าจะใช้งานฟีเจอร์ที่ไม่เสถียรได้ก็ตาม ยังแตกต่างจากวิธีการลดน้ำตาลโดยใช้มาตรฐานRanges คือไม่สูญเสียประสิทธิภาพจากการ.next()โทรหลายครั้ง ข้อเสียคือต้องใช้โค้ดสองสามบรรทัดในการตั้งค่าตัววนซ้ำดังนั้นอาจคุ้มค่าสำหรับโค้ดที่มีการวนซ้ำมาก ๆ


การเพิ่มประเภทอื่นUในตัวเลือกที่สองของคุณคุณสามารถใช้ประเภทที่รองรับการเพิ่มด้วยประเภทอื่นและยังคงได้รับ a T. เช่นเวลาและระยะเวลาโปรดจำไว้
Ryan

@Ryan ดูเหมือนจะเป็นความคิดที่ดีและควรใช้งานได้โดยมีโครงสร้างที่กำหนดไว้ดังนี้: struct StepRange <T> (T, T, U) โดยที่สำหรับ <'a,' b> & 'a T: เพิ่ม <&' b U, เอาท์พุท = T>, T: PartialOrd, T: โคลน; ซึ่งควรเผื่ออายุการใช้งานที่แตกต่างกันสำหรับประเภทอินพุต T และ U
GordonBGood


3

คุณจะเขียนรหัส C ++ ของคุณ:

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

... ใน Rust เป็นเช่นนั้น:

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

ฉันคิดว่าเวอร์ชั่น Rust ก็อ่านได้มากกว่าเช่นกัน


เรื่องการแทรก "ดำเนินการต่อ" ในลูปเราคิดว่าสิ่งนี้จะทำได้ภายในสาขาเงื่อนไขเท่านั้นแม้ในโครงสร้างสำหรับฉันคิดว่า ถ้าเป็นเช่นนั้นฉันคิดว่าการเพิ่มภายในสาขาเงื่อนไขในโครงสร้าง while ก็เป็นเรื่องปกติก่อนที่จะ "ดำเนินการต่อ" -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();
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.