RxKotlinでFizzBuzz

FizzBuzzネタとRxネタです。
Rxのおさらいをしながら、FizzBuzzとRxについて理解を深めていきましょう。
ではおさらいから始めます。



### RxKotlinについておさらい

RxJavaのKotlin用の薄いラッパーライブラリです。


### RxJavaについてのおさらい

言わずと知れたリアクティブプログラミングのための言語の枠を超えた拡張ライブラリ"Reactive Extensions(通称Rx)"のJava版です。


### Rxについて

これも最近では言わずと知れたと言えそうですが、~~Promiseの代用だよと思って使われていた方々にはあまり理解されていなさそうなので~~改めて説明していくとします。

##### Rxの宣言的特徴について

Rxとはプログラム上の宣言と、実際の動作のタイミングが全く違うコードスタイルのライブラリです。（これを宣言的と言うみたいなんですが、この説明についてあまり詳しい記事は正直あまりみたことがないです。ほとんどのサンプルコードもほとんど毎回直接関数を呼び出してそこで、宣言し、そこで実行させているものばかりでした。宣言的なコードという説明としては不十分だったのではないでしょうか？）
じゃ実際どう言うことなのかって言うと。プログラムのコードで書きますね。

```kotlin
class SayHello {
    val input = PublishProcessor.create<Unit>()
    val output: Flowable<String>
    init {
        // inputとoutputの関係を宣言
        // inputのシグナルがきたら "Hello, World"にしてoutputに渡す
        output = input.map { "Hello, World" }
    }
}

class UsingSayHello {
    val sayHello = SayHello()
    init {
        // outputのシグナルが来た時の関係を宣言
        val print = { str: String -> println(str) }
        // outputがシグナルを受信したらprintを実行させる
        sayHello.output.subscribeBy(onNext = print)
    }
    fun sayHello() {
        // 実行タイミングで必要なのはonNextの呼び出しだけ
        sayHello.input.onNext(Unit)
    }
}
// 実行
fun main() {
    val hello = UsingSayHello()
    hello.sayHello() // Hello, World
    hello.sayHello() // Hello, World
}
```

コードをみていただけばわかる通り、Kotlinのクラスでinitブロックで実行したいコードが書かれています。Rxを使わないケースでこんなコードはなかなか見ることはないのではないでしょう。

そして実際に発動させる`sayHello`関数の中身のコードには`onNext()`だけが呼ばれていて、実行内容のコードがありません。

このように、実行可能にするタイミングと、実際にそれを実行させるタイミングが分離できるプログラミングを宣言的プログラミングと言いい。これがRxの一つの特徴となります。これを間違えると、「実行できるようにプログラムしたと思っても実行されるようになっていなかった」、「毎回実行時に宣言と実行を行うので処理が余計で遅くなる」など、色々と痛い目に遭うことがあると思うので、これをしっかり肝に命じておくべきでしょう。

##### 合成的特徴について

これはよく説明を見るのでここで深く説明する必要なはいとおもいますが。Rxの最大の特徴とされているのはシグナル（Streamから流れてくる信号のようなイメージ）の分岐や、いくつかのシグナルの合成です。今回はその中で`zipWith`関数をつかってFizzとBuzz二つに分岐したStreamを再合成合成して出力するところを見せたいと思います。

では以降、本題のFizzBuzzの話に入っていきましょう。

### FizzBuzzについておさらい

> プレイヤーは円状に座る。最初のプレイヤーは「1」と数字を発言する。次のプレイヤーは直前のプレイヤーの次の数字を発言していく。ただし、3で割り切れる場合は「Fizz」（Bizz Buzzの場合は「Bizz」）、5で割り切れる場合は「Buzz」、両者で割り切れる場合（すなわち15で割り切れる場合）は「Fizz Buzz」（Bizz Buzzの場合は「Bizz Buzz」）を数の代わりに発言しなければならない。発言を間違えた者や、ためらった者は脱落となる。
出典： フリー百科事典『ウィキペディア（Wikipedia）』


この話今調べて初めて知ったんですが、まぁそうだよねってくらいの感想でした。
このゲームをプログラムで書くとどうなるのか？というのが元なんだろうな？くらいの感想ですね。
プログラマを採用する際の足切りのテストで、単純な条件分岐すらできない自称プログラマがいるという、一聞するとジョークかと思ってたら、結構実際にやってみると足切りに使えるんだって話が元ネタだった記憶があります。

じゃぁなんでFizzBuzzが話題に上がりやすいかって、詳細まではりませんけど、FizzBuzzというネタが最近の日本？では結構有名になっていて、説明しなくても共通のネタとして話が通りやすいのと、これをつまみに話をすると承認欲求がみたされやすいとかあるんじゃないでしょうか？

正直これを書いている時点で僕自身どうなんだって話ではあるんですけど、承認欲求があっちゃ悪いって話でもないので、グダグダ言ってないで先行きます。


### RxをつかったFizzBuzzの実装

まずさっきのコードを確認し、宣言的特徴と、実行させる方法の二つについて確認しましょう。

```kotlin
class SayHello {
    val input = PublishProcessor.create<Unit>()
    val output: Flowable<String>
    init {
        output = input.map { "Hello, World" }
    }
}

class UsingSayHello {
    val sayHello = SayHello()
    init {
        val print = { str: String -> println(str) }
        sayHello.output.subscribeBy(onNext = print)
    }
    fun sayHello() {
        sayHello.input.onNext(Unit)
    }
}
// 実行
fun main() {
    val hello = UsingSayHello()
    hello.sayHello() // Hello, World
    hello.sayHello() // Hello, World
}
```

これが土台になります。このコードを以下のようにして、FizzBuzzの部分だけ作るようにしてみます。

```kotlin
class FizzBuzz {
    // UnitではなくIntへ
    val input = PublishProcessor.create<Int>()
    val output: Flowable<String>
    init {
        // TODO FizzBuzzのStreamを作成し、OutputにStringで渡したい
    }
}

class UsingFizzBuzz {
    val fizzBuzz = FizzBuzz()
    init {
        val print = { str: String -> println(str) }
        fizzBuzz.output.subscribeBy(onNext = print)
    }
    // 渡されたIntRangeを使ってFizzBuzzを出力する
    fun fizzBuzz(range: IntRange) = range.toFlowable().subscribe(fizzBuzz.input)
}
// 実行
fun main() {
    val fizzBuzz = UsingFizzBuzz()
    fizzBuzz.fizzBuzz(1..100)
}
```
こんな感じで、FizzBuzzの部分だけをinitブロックで宣言的に実装すればいいようにしてみました。

まずFizzとBuzzのStreamを作ってみます。

```kotlin
class FizzBuzz {
    val input = PublishProcessor.create<Int>()
    val output: Flowable<String>
    init {
        val fizz = input.map { if (it % 3 == 0) "Fizz" else "" }
        val buzz = input.map { if (it % 5 == 0) "Buzz" else "" }
        // TODO fizzbuzzを作ってoutputに渡す
    }
    ..
}
```
できました。
単にinputを`map`関数の中で普通のFizzBuzzと同様条件にあったら文字列を返すものを二つ作りました。

つぎに二つのStreamがシグナルを返すまで待って、一つのシグナルに合成する`zipWith`を使って、一度分岐したFizzBuzzを合成してみます。

```kotlin
class FizzBuzz {
    val input = PublishProcessor.create<Int>()
    val output: Flowable<String>
    init {
        val fizz = input.map { if (it % 3 == 0) "Fizz" else "" }
        val buzz = input.map { if (it % 5 == 0) "Buzz" else "" }
        val fizzBuzz = fizz.zipWith(buzz, { fz: String, bz: String -> fz + bz })
        // TODO outputに渡す
    }
    ..
}
```

最後に outputに文字列を渡します。

```kotlin
class FizzBuzz {
    val input = PublishProcessor.create<Int>()
    val output: Flowable<String>
    init {
        val fizz = input.map { if (it % 3 == 0) "Fizz" else "" }
        val buzz = input.map { if (it % 5 == 0) "Buzz" else "" }
        val fizzBuzz = fizz.zipWith(buzz, { fz: String, bz: String -> fz + bz })
        // 元のint値が欲しいので合成 num:fizbuzzの結果として出力
        output = input.zipWith(fizzBuzz, { num, fb -> "$num:$fb" })
    }
    ..
}
```

これでできました。

全体のコードはこれです。


```kotlin
class FizzBuzz {
    val input = PublishProcessor.create<Int>()
    val output: Flowable<String>

    init {
        val fizz = input.map { if (it % 3 == 0) "Fizz" else "" }
        val buzz = input.map { if (it % 5 == 0) "Buzz" else "" }
        val fizzBuzz = fizz.zipWith(buzz, { fz: String, bz: String -> fz + bz })
        output = input.zipWith(fizzBuzz, { num, fb -> "$num:$fb" })
    }
}

class UsingFizzBuzz {
    val fizzBuzz = FizzBuzz()

    init {
        val print = { str: String -> println(str) }
        fizzBuzz.output.subscribeBy(onNext = print)
    }

    fun fizzBuzz(range: IntRange) = range.toFlowable().subscribe(fizzBuzz.input)
}

// 実行
fun main() {
    val fb = UsingFizzBuzz()
    fb.fizzBuzz(1..100)
}
```

では実行してみましょう！！

```
1:
2:
3:Fizz
4:
5:Buzz
6:Fizz
7:
8:
9:Fizz
10:Buzz
11:
12:Fizz
13:
14:
15:FizzBuzz
16:
17:
18:Fizz
19:
20:Buzz
21:Fizz
22:
23:
24:Fizz
25:Buzz
26:
27:Fizz
28:
29:
30:FizzBuzz
...
```
ちゃんと出力されているようですね！


### おさらい


・・・おわかりいただけただろうか？


今回のコード、実は全然非同期処理は書いていません。Schedulersやスレッドに関する処理は一切出てきていないんです。
Rxは非同期処理のためのライブラリではない、単なるPromiseやFutureのような非同期処理のための実装の代替ではないことを示したかったのです。恐らくそららの代替に該当するのはKotlin Coroutinesではないかと思います。
Rxで書かれたコードはinput側からの反応によってシグナルが送信され、宣言されたコードが分岐、合成されながら実行されます。
つまりRxは宣言的なコードとStreamの合成を利用した、シグナルの流れと変化を、コードで示すように作られたライブラリなのです。ただしコードに出されている表示的な意味とは実行内容が異なることが発生するケースもあったりして、なかなかその辺は難しいのですが、またの機会があればお話させていただこうと思います。

以上。