「RxJava フィボナッチ」でググるといくつか記事が出てきますが、あまりイケてるのがなかったので自分で書いてみました。
いくつかのオペレータを使うので、RxJavaの勉強には良さそうです。
値を保持するクラスを作成
今回は、1000より小さいフィボナッチ数列を出力します。
public class Fibonacci {
/**
* フィボナッチ数列の値
*/
private final BehaviorSubject<Integer> value = BehaviorSubject.create();
/**
* 値をObservableで取得する
*/
public Observable<Integer> getValue() {
return value.asObservable();
}
/**
* 値をセットする
*/
public void setValue(int i) {
value.onNext(i);
}
/**
* 値の計算元となる2つのObservableをセットする
*/
public void setSource(Observable<Integer> observable0, Observable<Integer> observable1) {
Observable.zip(observable0, observable1, (i, j) -> i + j) // 値が来たら足し算する
.filter(i -> i < 1000) // 1000以上では止まるようにする
.subscribe(value::onNext); // 値を更新する
}
}
呼び出し側
通常、subscribeを循環させると無限ループになってしまうのでやりませんが、ここではあえてそうすることで、値を次々に計算していきます。
public static void main(String[] args) {
// 初期化
Fibonacci a = new Fibonacci();
Fibonacci b = new Fibonacci();
Fibonacci c = new Fibonacci();
// 値を出力する(※1)
a.getValue().subscribe(System.out::println);
b.getValue().subscribe(System.out::println);
c.getValue().subscribe(System.out::println);
// 値の計算元となる2つのObservableをセットする
a.setSource(b.getValue(), c.getValue());
b.setSource(c.getValue(), a.getValue().skip(1)); // (※2)
c.setSource(a.getValue(), b.getValue());
// 初期値をセットする
a.setValue(0);
b.setValue(1);
}
※1 必ず先に出力設定
値更新より前に出力設定をしないと、出力する前にどんどん値が更新されてしまいます。
※2 最初の値はスキップする
最初のbの計算では、aの初期値である0でなく、b+cが格納された後の2を使いたいので、1回だけスキップします。
(本当はここもスマートに書きたい)
出力
0
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
考察
プログラムを書いていると、値を全て知っている人が現れるケースがしばしばあると思いますが、Subjectを使うことで、それぞれの項が自発的に値を計算し、それを伝搬している点で、責任が分散していていいかなと思いました。
似たような方法で、物理演算(波とか)もできるかなーとか思いました。
質問・ご意見があればお気軽にどうぞ!