Rx (ReactiveExtension)の事始めとして、RxJSを使ってFizzBuzzしてみる。

準備

<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/3.1.2/rx.all.compat.js"></script>

ケース1:range, mapを使う

1~100の数値を流すストリームを作成し、それをmapで変換してみる。

var countStream = Rx.Observable.range(1, 100);
var convertedStream = countStream.map(function(x) {
    if ((x % 15) == 0)
        return 'FizzBuzz';
    if ((x % 3) == 0)
        return 'Fizz';
    if ((x % 5) == 0)
        return 'Buzz';
    return x;
});
var output = convertedStream.subscribe(function(x) {
    $('<span>' + x + '</span><br />').appendTo($('#console'));
});

ケース2:filterを使う

ケース1では1つのストリームでFizzやBuzzへの変換を行っていたが、Fizzのケースだけ流すストリーム、Buzzのケースだけ流すストリームという具合に分岐させ、それらを合流させて1つのストリームに戻してみる。

var isFizzBuzz = function(x) { return (x % 15) == 0; };
var isFizz = function(x) { return (x % 3) == 0 && !isFizzBuzz(x); };
var isBuzz = function(x) { return (x % 5) == 0 && !isFizzBuzz(x); };

var countStream = Rx.Observable.range(1, 100);
var fizzStream = countStream.filter(isFizz).map(function(_) { return 'Fizz'; });
var buzzStream = countStream.filter(isBuzz).map(function(_) { return 'Buzz'; });
var fizzBuzzStream = countStream.filter(isFizzBuzz).map(function(_) { return 'FizzBuzz'; });
var otherStream = countStream.filter(function(x) {
    return !isFizz(x) && !isBuzz(x) && !isFizzBuzz(x);
}).map(function(x) {
    return x;
});

var mergedStream = Rx.Observable.merge(fizzStream, buzzStream, fizzBuzzStream, otherStream);
var output = mergedStream.subscribe(function(x) {
    $('<span>' + x + '</span><br />').appendTo($('#console'));
});
  • 実行: https://jsfiddle.net/n6kpvpdp/
  • filterで条件にあうものだけを流すストリームに変換できる
  • mergeで複数のストリームを合流させる

ケース3:Subjectを使う

以前のケースのように、先に1~100を出力するストリームを作っておいて、というのが気に食わない。 後から流すものを指定したい。 そういう場合にSubjectが使える。

var isFizzBuzz = function(x) { return (x % 15) == 0; };
var isFizz = function(x) { return (x % 3) == 0 && !isFizzBuzz(x); };
var isBuzz = function(x) { return (x % 5) == 0 && !isFizzBuzz(x); };

var subject = new Rx.Subject();
var fizzStream = subject.filter(isFizz).map(function(_) { return 'Fizz'; });
var buzzStream = subject.filter(isBuzz).map(function(_) { return 'Buzz'; });
var fizzBuzzStream = subject.filter(isFizzBuzz).map(function(_) { return 'FizzBuzz'; });
var otherStream = subject.filter(function(x) {
    return !isFizz(x) && !isBuzz(x) && !isFizzBuzz(x);
}).map(function(x) {
    return x;
});

var mergedStream = Rx.Observable.merge(fizzStream, buzzStream, fizzBuzzStream, otherStream);
var output = mergedStream.subscribe(function(x) {
    $('<span>' + x + '</span><br />').appendTo($('#console'));
});

Rx.Observable.range(1, 100).subscribe(subject);
  • 実行: https://jsfiddle.net/n6kpvpdp/
  • SubjectはObservable sequenceでありObserverである とのこと
  • Subject を作ってそのストリームを filter, map するようにしておいて、 subject に対して流したいものを subscribe してやる

参考