MATHGRAM

主に数学とプログラミング、時々趣味について。

[Rust] ビルダーパターンを理解する

ちょっと気になったのでまとめておきます.
実際使われてるんですかね? まだRust歴が短すぎてよくわからないですけど一応使えるようにしておきたい.

pythonとの対比

いきなりRustの話じゃなくて申し訳ないのですが,
例えばpythonであるクラスを作り, メンバー変数の初期値を設定しようとするとこんな感じになります.

class CL(object):
    def __init__(self, a, b, c=10, d=True):
        self.a = a
        self.b = b
        self.c = c
        self.d = d

    def show(self):
        print(self.a)
        print(self.b)
        print(self.c)
        print(self.d)

def main():
    cl = CL(3, "hoge")
    cl.show()

main()

出力

3
hoge
10
True

メンバー変数の初期値としてcやdは値を持っているので問題なく動きますね.
もちろんc, dに他の変数を代入することだって可能です.

Rustで構造体を定義する時も同じようなことが可能であり, それらを実現する機能をBilderパターンと呼びます.

以前の記事で書いた正規分布の構造体を例に挙げて見ましょう.

初期値を平均0, 分散1に設定しておく

正規分布を使う時は大体が標準正規分布であるはずなので, わざわざユーザーに 1 や 0 を入力させるのは良くないかもしれません.
そこでデフォルトでは平均0, 分散1 になるように設定してあげましょう.

// 目的の構造体
// 省略してますが, この構造体に実際に使うメソッドを実装していきます.
struct NormalDistribution {
    mu : f64,
    sigma : f64,
}

// 初期設定専用の構造体
// ここには初期設定したい変数それぞれに対しメソッドを実装します.
struct NormalDistributionBilder {
    mu: f64,
    sigma: f64,
}

impl NormalDistributionBilder {
    fn new() -> NormalDistributionBilder {
        NormalDistributionBilder{mu: 0f64, sigma: 1f64,}
    }

    fn mu(&mut self, mu: f64) -> &mut NormalDistributionBilder {
        self.mu = mu;
        self
    }

    fn sigma(&mut self, sigma: f64) -> &mut NormalDistributionBilder {
        self.sigma = sigma;
        self
    }

    fn set(&mut self) -> NormalDistribution {
        NormalDistribution{ mu: self.mu, sigma: self.sigma}
    }
}

イメージは初期設定専用の構造体を作るって感じでしょうか. 設定したい変数それぞれに関数を作り, 最後にそれらの変数を目的の型に入れて返す関数を作ればBilderパターンの完成です( set()関数がそれにあたります ).

実際に使うとこんな感じです.

//そのままset()すると,
//平均 0, 分散 1 のNormalDistributionが生成される.
let mut norm = NormalDistributionBilder::new().set();

//平均 1, 分散 3 のNormalDistributionが生成される.
let mut norm = NormalDistributionBilder::new()
                                         .mu(1f64)
                                         .sigma(3f64)
                                         .set(); 

まとめ

Bilderパターンに関してまとめました. 初期値の設定をしなくて済むようになるのはとても大切なことだと思います. 例えば最適化手法などのlearning rateをユーザーが必ず入力しなければいけない, なんてことがなくなりますからね. 多くのユーザーが同じパラメータを使うことが想定されている場合, 初期値の設定は必須でしょう.

しかし, 初期値のためだけに新しい構造体を作り, さらに set() 関数などの最終的に型の受け渡しをするメソッドを作るのもちょっと面倒な気がします. これから勉強していく中で, ありがたみに触れることができたらいいですね. もしハッキリとした使い所がわかったら更新したいと思います.

以上です.