blog.ryota-ka.me

Rustでジェネレータを作ったり,遅延評価してみる

はじめに#

日本時間の2015年5月16日にRust 1.0がリリースされた.というわけで,4月末頃から延々と「書こう書こう」と言っていた記事を,いい加減に書こうと思う.

これまでの流れ#

イテレータの例#

fn main() {
    let arr = [0, 1, 2, 3];  // 要素は4つしかない
    let mut iter = arr.iter();

    for _ in 0..5 {  // ループを5回回してみる
        match iter.next() {
            Some(x) => println!("{}", x),
            None    => println!("I don't have values anymore!")
        }
    }
}

このコードの出力結果は以下のようになる.

0
1
2
3
I don't have values anymore!

イテレータの内部に返すべき値xが残っていれば,Some(x)を,残っていなければNoneを返している様子がわかる.このように,Rustのイテレータのnext()メソッドは一般にOption<T>を返す.一方,for ... inは,イテレータからNoneを得た際に自動的にループを終了する.こちらも簡単な例で示しておく.

fn main() {
    let arr = [0, 1, 2, 3];
    let iter = arr.iter();
    for i in iter {  // 0 1 2 3 と順に表示する
        println!("{}", i)
    }
}

もちろん,上の例で示したように,for i in 0..4 { ... }と書くこともできる.また,next()メソッドなどの,イテレータ内部の状態を変化させるメソッドを使用しない場合,変数にmutキーワードを付けて宣言する必要はない.

Rustのイテレータについて#

外部イテレータのためのインターフェースとして,std::iter::Iteratorというトレイト*1が用意されており,このトレイトを実装したデータ型は,イテレータとして振る舞うことができる*2.データ型に対してこのトレイトを実装する手順は以下の2つである.

  • Itemassociated typeでイテレータが返す値の型を宣言する
  • next()メソッドを実装する

next()メソッドの型シグネチャはfn next(&mut self) -> Option<Self::Item>となっている.

フィボナッチ数列を返すイテレータ#

実際に,整数の二つ組を持った構造体Fibonacciに対して,Iteratorトレイトを実装してみよう.

struct Fibonacci {
    state: (u64, u64)
}

impl Iterator for Fibonacci {
    type Item = u64;

    fn next(&mut self) -> Option<u64> {
        let (a, b) = self.state;
        self.state = (b, a + b);

        Some(a)
    }
}

impl Fibonacci {
    fn new() -> Fibonacci {
        Fibonacci { state: (0, 1) }
    }
}

next()メソッドを実行する度に,イテレータが内部の状態を変えつつ,Option<u64>型の値を返す様子がわかる.また,new()associated functionで,最初の2項である0, 1を持ったFibonacci構造体を作って返すようにした.

fn main() {
    let mut fib = Fib::new();

    println!("{}", fib.next().unwrap()); // 0
    println!("{}", fib.next().unwrap()); // 1
    println!("{}", fib.next().unwrap()); // 1
    println!("{}", fib.next().unwrap()); // 2
    println!("{}", fib.next().unwrap()); // 3
}

また,main()関数の中身を以下のように書き換えてみよう.

fn main() {
    for i in Fibonacci::new()
        .map(|x| x.pow(2))
        .filter(|x| x % 2 == 1)
        .take(10)
    {
        println!("{}", i);
    }
}

このコードの出力結果は以下のようになる.

1
1
9
25
169
441
3025
7921
54289
142129

脚注#

*1: Haskellにおける型クラスのようなものと考えてもらえばよい.

*2: 詳細は前述のドキュメントを参照のこと.