Kotlin で FizzBuzz ／ Sequence を使う

Kotlin らしい FizzBuzz の例。

```Kotlin
fun main() {
    (1..100)
        .map {
            when {
                it % 3 == 0 && it % 5 == 0 -> "FizzBuzz"
                it % 3 == 0 -> "Fizz"
                it % 5 == 0 -> "Buzz"
                else -> it.toString()
            }
        }
        .forEach {
            println(it)
        }
}
```

## 解説

```Kotlin
    (1..100)
```

2項演算子 [`..`](https://kotlinlang.org/docs/reference/operator-overloading.html#binary-operations) は [`rangeTo` 関数に変換される](https://kotlinlang.org/docs/reference/ranges.html)。
左項が [`Int`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/index.html) 型であれば、[`rangeTo`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-int/range-to.html) 関数の返値は [`IntRange`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.ranges/-int-range/index.html) 型。
したがって `1..100` は `IntRange(1, 100)` と等価。

[`IntRange`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.ranges/-int-range/index.html) クラスは [`Iterable`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-iterable/index.html) インターフェイスを実装した [`IntProgression`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.ranges/-int-progression/index.html) クラスを継承しており、指定された範囲の整数を1刻みで順に取得できる。
この場合だと `1`, `2`, `3`, ... , `100`。

```Kotlin
        .map {
```

[`Iterable`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-iterable/index.html) 型の拡張関数 [`map`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/map.html#kotlin.collections$map(kotlin.collections.Iterable((kotlin.collections.map.T)),%20kotlin.Function1((kotlin.collections.map.T,%20kotlin.collections.map.R)))) は、 [`Iterable`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-iterable/index.html) オブジェクトの各要素から変換された要素を持つ [`List`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html) オブジェクトを生成する。
（この例では `1`, `2`, `3`, ..., `100` という数値を要素とする [`Iterable`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-iterable/index.html) オブジェクト から、`"1"`, `"2"`, `"Fizz"`, ..., `"Buzz"` という文字列を要素とする [`List`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html) を生成する。）
その変換処理は `map` の後ろのブロック `{` `}` 内に記述する。
このブロック内において要素はデフォルトでは暗黙の変数 `it` で表される。

```Kotlin
            when {
                it % (3 * 5) == 0 -> "FizzBuzz"
                it % 3 == 0 -> "Fizz"
                it % 5 == 0 -> "Buzz"
                else -> it.toString()
            }
```

[`when`](https://kotlinlang.org/docs/reference/control-flow.html#when-expression) を用いて、各要素をその値に応じた文字列に変換する。
上に書いたものが優先されるため、 要素の値が 15 の倍数の場合は `"Fizz"` や `"Buzz"` とはならずに、 `"FizzBuzz"` となる。
`when` からの返値を文字列型に統一するため、3 と 5 のいずれの倍数でもない場合は [`toString`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-any/to-string.html) 関数で数値を文字列に変換する。

`when` は `map` のブロックの最後の式なので、 `when` の返値が `map` のブロックの返値となる。

```Kotlin
        .forEach {
```

[`List`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html) クラスも [`Iterable`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-iterable/index.html) インターフェイスを実装しているため、
[`Iterable`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-iterable/index.html) 型の拡張関数 [`forEach`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/for-each.html#kotlin.collections$forEach(kotlin.collections.Iterator((kotlin.collections.forEach.T)),%20kotlin.Function1((kotlin.collections.forEach.T,%20kotlin.Unit)))) を用いて各要素に対して処理を行う。

[`map`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/map.html#kotlin.collections$map(kotlin.collections.Iterable((kotlin.collections.map.T)),%20kotlin.Function1((kotlin.collections.map.T,%20kotlin.collections.map.R)))) と同様、処理は `forEach` の後ろのブロック `{` `}` 内に記述する。
このブロック内において要素はデフォルトでは暗黙の変数 `it` で表される。

```Kotlin
            println(it)
```

[`println`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/println.html) 関数は与えられた引数を文字列に変換し、末尾に改行を付けて、標準出力に出力する。

<details><summary>実行結果（標準出力）</summary>
```
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz
Fizz
22
23
Fizz
Buzz
26
Fizz
28
29
FizzBuzz
31
32
Fizz
34
Buzz
Fizz
37
38
Fizz
Buzz
41
Fizz
43
44
FizzBuzz
46
47
Fizz
49
Buzz
Fizz
52
53
Fizz
Buzz
56
Fizz
58
59
FizzBuzz
61
62
Fizz
64
Buzz
Fizz
67
68
Fizz
Buzz
71
Fizz
73
74
FizzBuzz
76
77
Fizz
79
Buzz
Fizz
82
83
Fizz
Buzz
86
Fizz
88
89
FizzBuzz
91
92
Fizz
94
Buzz
Fizz
97
98
Fizz
Buzz
```
</details>

## 発展（[`Sequence`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/-sequence/index.html) を使う）

```Kotlin
fun main() {
    (1..100)
        .asSequence() // この行を追加
        .map {
            when {
                it % (3 * 5) == 0 -> "FizzBuzz"
                it % 3 == 0 -> "Fizz"
                it % 5 == 0 -> "Buzz"
                else -> it.toString()
            }
        }
        .forEach {
            println(it)
        }
}
```

[`Iterable`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-iterable/index.html) 型の拡張関数 [`map`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/map.html#kotlin.collections$map(kotlin.collections.Iterable((kotlin.collections.map.T)),%20kotlin.Function1((kotlin.collections.map.T,%20kotlin.collections.map.R)))) は [`List`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html) を生成する。
つまり、すべての要素のインスタンスを生成する。
今回のように [`List`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html) を生成するのが [`map`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/map.html#kotlin.collections$map(kotlin.collections.Iterable((kotlin.collections.map.T)),%20kotlin.Function1((kotlin.collections.map.T,%20kotlin.collections.map.R)))) 1回だけであればほぼ影響はないが、[kotlin.collections パッケージにある拡張関数群](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/index.html#functions) をチェインして処理をするような場合、関数呼び出しごとに [`List`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html) が生成されるので非常に効率が悪い。

そのような場合にはできるだけ早い段階で [`Iterable`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-iterable/index.html) 型の拡張関数 [`asSequence`](kotlin.sequences$asSequence(kotlin.collections.Iterator((kotlin.sequences.asSequence.T)))) を用いて [`Sequence`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/-sequence/index.html) 型に変換してやるとよい。
（[`List`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html) クラスも [`Iterable`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-iterable/index.html) インターフェイスを実装している。）

[`Sequence`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/-sequence/index.html) クラスは [`Iterator`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-iterator/index.html) オブジェクトを返すメソッド [`iterator`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/-sequence/iterator.html) を持っており、要素が1つ要求されるごとに1つ取得して（生成して）返すため、効率が良い。

[kotlin.sequences パッケージの拡張関数群](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/index.html#functions) には [`map`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/map.html) や [`forEach`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/for-each.html) など、[kotlin.collections パッケージの拡張関数群](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/index.html#functions) と同様のものが多数用意されている。（すべてではないが。）
そのため多くの場合は単に [`asSequence`](kotlin.sequences$asSequence(kotlin.collections.Iterator((kotlin.sequences.asSequence.T)))) 関数呼び出しを追加してやるだけで恩恵を受けられる。

### 注意

ただし注意すべきことがある。
それは次の２点である。

- 要素を要求しない限り要素が生成されない
- 要素を要求するたびに要素が生成される

例えば次のような処理を考える。

```Kotlin
class MyClass {
    companion object {
        /** 生成したインスタンスの数。 */
        var count: Int = 0
            private set
    }

    init {
        ++count
    }

    fun doMyWork() {}
}

fun main() {
    (1..100)
        .asSequence()
        .map {
            MyClass()
        }
        .forEach {
            it.doMyWork()
        }
    println(MyClass.count) // > 100
}
```

これは期待通り `100` を出力する。

では `main` 関数を次のように変えるとどうだろうか。

```Kotlin
fun main() {
    val sequence = (1..100)
        .asSequence()
        .map {
            MyClass()
        }
        // forEach がない。
    println(MyClass.count) // > 0
}
```

なんと、これだと出力値は 0 になる。
要素を要求する [`forEach`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/for-each.html) がないため、`map` のブロックが一度も呼ばれないからだ。

では次は？

```Kotlin
fun main() {
    val sequence = (1..100)
        .asSequence()
        .map {
            MyClass()
        }
    sequence.forEach { it.doMyWork() } // forEach が…
    sequence.forEach { it.doMyWork() } // 2回
    println(MyClass.count) // > 200
}
```

なんと！　これは 200 を出力する。
[`forEach`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/for-each.html) を2回呼び出したことで、各要素が2回ずつ要求されたからだ。

このような時は拡張関数 [`toList`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/to-list.html) を使って [`List`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/index.html) オブジェクトに変換してやると良い。
（この例の場合だと、そもそも [`asSequence`](kotlin.sequences$asSequence(kotlin.collections.Iterator((kotlin.sequences.asSequence.T)))) を使って [`Sequence`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/-sequence/index.html) オブジェクトにする必要がなくなるのだが。）

```Kotlin
fun main() {
    val list = (1..100)
        .asSequence()
        .map {
            MyClass()
        }
        .toList()
    list.forEach { it.doMyWork() }
    list.forEach { it.doMyWork() }
    println(MyClass.count) // > 100
}
```

このように、[`Sequence`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/-sequence/index.html) クラスは効率は良いのだが使い方を間違えるとバグを生んでしまう。
慣れるまでは、上の例のように、 [`asSequence`](kotlin.sequences$asSequence(kotlin.collections.Iterator((kotlin.sequences.asSequence.T)))) 関数を使ったら必ず [`toList`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/to-list.html) 関数で終える、とした方が良いかもしれない。