カスタムステップを使用して範囲を反復処理するにはどうすればよいですか?


100

Rustの範囲を1以外のステップで反復するにはどうすればよいですか?私はC ++のバックグラウンドから来ているので、次のようなことをしたいと思います。

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

Rustではrange関数を使用する必要がありますが、カスタムステップを使用するために使用できる3番目の引数がないようです。どうすればこれを達成できますか?

回答:


136

range_step_inclusiveそしてrange_step、長い間なくなっています。

Rust 1.28の時点で、Iterator::step_by安定しています。

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


この方法は、32ビットサイズタイプのマシンでは64ビットステップをサポートしないことに注意してください。
user202729

12

.step_byメソッドが安定するまで、IteratorRangeとにかく実際にあることです)を使用して、必要なことを簡単に達成できるように思われます。

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、2番目のオプションに、別の型での追加をサポートする型を使用しても、を生成できTます。たとえば、時間と期間が思い浮かびます。
ライアン

@Ryan、これは良い考えのようで、次のように定義された構造体で機能するはずです。structStepRange <T>(T、T、U)where for <'a、' b>& 'a T:Add <&' b U、出力= T>、T:PartialOrd、T:クローン; これにより、入力TタイプとUタイプの寿命が異なります。
GordonBGood 2017年


3

あなたはあなたのC ++コードを書くでしょう:

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

...そのような錆で:

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

Rustバージョンの方が読みやすいと思います。


Re:ループに「continue」を挿入すると、for構造でも条件分岐内でのみこれを行うと思います。もしそうなら、「続行」する前にwhile構造の条件分岐内でインクリメントしても問題ないと思います。そうすれば、意図したとおりに機能します。それとも私は何かを見落としていますか?
WDS

1
@WDSは、言語の基本機能をcontinue正しく機能させるための直感に反する忙しい作業です。それは可能ですが、この設計はバグを助長します。
Chai

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.