【Reactive Extensions】 IObservableの合成と分岐入門その5
【Reactive Extensions】 IObservableの合成と分岐入門その5
おはこんばんにちは、tsuchimotoです。
今回は前回の第4回に引き続き、UniRxを使ったIObservableの合成と分岐入門の5回目になります。
前回までのリンク
- IObservableの合成と分岐入門その1(Concat, StartWith)
- IObservableの合成と分岐入門その2(Merge)
- IObservableの合成と分岐入門その3(SelectMany)
- IObservableの合成と分岐入門その4(Zip, CombineLatest)
Ambメソッド
Ambメソッドについて説明します。
Ambメソッドは複数のIObservableのシーケンスの中から一番最初に値を発行したIObservableのシーケンスの値を後続に流すメソッドです。
このメソッドのオーバーロードを以下に示します。
// 引数で渡した全てのIObservable<T>から最初に値を発行したIObservable<T>の値を後ろに流す public static IObservable<T> Amb<T>(params IObservable<T>[] sources); // sources内の全てのIObservable<T>から最初に値を発行したIObservable<T>の値を後ろに流す public static IObservable<T> Amb<T>(this IEnumerable<IObservable<T>> sources); // firstとsecondのうち最初に値を発行したものの値を後ろに流す public static IObservable<T> Amb<T>(this IObservable<T> first, IObservable<T> second);
他の合成系メソッドと同様に可変長引数やIEnumerable>の拡張メソッドや2つの合成に特化したオーバーロードが用意されています。
Ambメソッドのコード例を以下に示します。
Observable.Amb( // 3秒後に値を発行するIO<T> Observable.Timer(TimeSpan.FromSeconds(3)).Select(_ => "3sec"), // 10秒後に値を発行するIO<T> Observable.Timer(TimeSpan.FromSeconds(10)).Select(_ => "10sec"), // 2秒後に値を発行するIO<T> Observable.Timer(TimeSpan.FromSeconds(2)).Select(_ => "2sec"), // 30秒後に値を発行するIO<T> Observable.Timer(TimeSpan.FromSeconds(22)).Select(_ => "30sec"), // 5秒後に値を発行するIO<T> Observable.Timer(TimeSpan.FromSeconds(6)).Select(_ => "5sec")) .Subscribe( s => Debug.LogFormat("OnNext: {0}", s), () => Debug.Log("OnCompleted"));
Timerメソッドを使って、指定した時間が経過した後に、値を発行するIObservableのシーケンスをAmbメソッドで合成しています。
実行結果を以下に示します。
OnNext: 2sec OnCompleted
引数で渡した中で一番早く値を発行する Observable.Timer(TimeSpan.FromSecond(2)).Select(_ => "2sec")の結果が OnNextやOnCompletedに渡っていることが確認できます。
Switchメソッド
Switchメソッドについて説明します。
Switchメソッドは複数のIObservableのシーケンスの中から値が発行される度に、一つ前のシーケンスの値を流すのを辞めて、 後から発行されるシーケンスの値を流し始めます。
要するに、後から発行されたIObservableシーケンスにどんどん切り替えていくのです。
メソッドの定義は以下のようになります。
public static IObservable<T> Switch<T>(this IObservable<IObservable<T>> sources);
Switchメソッドのコード例を以下に示します。
// 1から4の値を発行するシーケンス IObservable<string> source = Observable.Range(1, 4) // 1から4の値をそれぞれ1ミリ秒毎に文字列を発行する4つのシーケンスに変換 .Select(i => Observable.Interval(TimeSpan.FromMilliseconds(i)).Take(3).Select(sec => string.Format("i:{0} {1}MSec", i, sec) ) ) // Switchメソッドで合成 .Switch(); source.Subscribe( s => Debug.LogFormat("OnNext: {0}", s), () => Debug.Log("OnCompleted"));
実行結果は以下になります。
OnNext: i:4 0MSec OnNext: i:4 1MSec OnNext: i:4 2MSec OnCompleted
ここで比較のためにSwitchをMergeに変えて実行してみましょう。
コードは以下になります。
// 1から4の値を発行するシーケンス IObservable<string> source = Observable.Range(1, 4) // 1から4の値をそれぞれ1ミリ秒毎に文字列を発行する4つのシーケンスに変換 .Select(i => Observable.Interval(TimeSpan.FromMilliseconds(i)).Take(3).Select(sec => string.Format("i:{0} {1}MSec", i, sec) ) ) // Mergeメソッドで合成 .Merge();
実行結果は以下になります。
OnNext: i:1 0MSec OnNext: i:2 0MSec OnNext: i:3 0MSec OnNext: i:4 0MSec OnNext: i:1 1MSec OnNext: i:2 1MSec OnNext: i:3 1MSec OnNext: i:4 1MSec OnNext: i:1 2MSec OnNext: i:2 2MSec OnNext: i:3 2MSec OnNext: i:4 2MSec OnCompleted
Mergeメソッドでは4つのIObservableシーケンスから発行された値はすべて流されました。 対して、Switchメソッドでは4番目のシーケンスから発行された値しか流れていません。
Switchメソッドでは新たなシーケンスにどんどん切り替わって行き、最後の4番目のシーケンスの値しか流れて来ないことが分かります。