「IEnumerableの遅延評価」と「IObservableのCold-Hot」の共通点についてまとめた

#はじめに
LINQの「__`IEnumerable`の遅延評価__」と、ReactiveExtensionsの「__`IObservable`のHod-Cold__」は、どちらもLINQやRxを使い始めてしばらくするとぶち当たる壁ですね。避けては通れない道です。

先日、この両者にとある共通点を発見したので、記事にしてまとめました。


##想定読者
この記事は次のような方をターゲットにしています。

- LINQとRxをある程度使っている
- 遅延評価やHot-Coldについての理解が曖昧


#`IEnumerable`

まずは`IEnumerable`です。
コレクションクラスが必ず実装する必要があるインターフェイスですね。

##`IEnumerable`の遅延評価

`IEnumerable`の遅延評価とは、「__必要になるときまで計算しない__」という`IEnumerable<T>`インターフェイスがもつ性質のことです。

例えば、以下のようなコードがあるとします。
1~10までの整数のうち偶数のみを各2倍にして変数`enumerable`に格納して、それを`foreach`で回して出力するコードです。

```csharp
IEnumerable<int> enumerable =           //変数「enumerable」に
    Enumerable.Range(1, 10)             //1~10までのうち
              .Where(n => n % 2 == 0)   //偶数に絞り込んだものを
              .Select(n => n * 2);      //2倍にしたものを代入する

foreach (var num in enumerable)
{ //結果を出力
    Console.WriteLine(num);
}
```

もちろん結果は「4, 8, 12, 16, 20」の順に出力されるのですが、この「4, 8, 12, 16, 20」という結果は、__実は`foreach`で回したときに初めて計算されています__。

普通の感覚だと、変数`enumerable`にLINQメソッドチェーンを代入した時点で、`enumerable`には「4, 8, 12, 16, 20」という計算結果が入っていると考えがちです。
しかし、実際には__`enumerable`に代入した時点では、「4, 8, 12, 16, 20」という計算結果は入っていません__。
では何が入っているのかというと、__「1〜10のうち偶数に絞り込んでそれらを2倍にしてね」という「命令」が入っています__。

「命令」なので、計算自体はまだ実行しません。
ではいつ実行するのかというと、必要になったときに初めて計算を実行します。
つまり、__`foreach`で回したときになって初めて__、「1〜10のうち偶数に絞り込んでそれらを2倍にする」という命令が実行され、結果が出力されることになります。

これが、「__必要になるときまで計算しない__」という`IEnumerable`の遅延評価の特性です。

###遅延評価の注意点

さてこの遅延評価の注意点ですが、まだ評価されていない「命令」が入った`IEnumerable`を複数回`foreach`すると、__「命令」が複数回実行されてしまう__という点です。

```csharp
IEnumerable<int> enumerable =   //「命令」を代入
    Enumerable.Range(1, 10)             
              .Where(n => n % 2 == 0)   
              .Select(n => n * 2);      

foreach (var num in enumerable) //「命令」を実行
{ 
    Console.WriteLine(num);
}
foreach (var num in enumerable) //「命令」を実行
{ 
    Console.WriteLine(num);
}
```

`enumerable`に入っているのはあくまで「__命令__」なので、2回`foreach`すると、命令が2回実行されてしまいます。
これは明らかに無駄な処理ですね。本来であれば、この「命令」は1回実行すれば十分で、2回目以降は最初の実行結果を流用することができるはずです。

この特性を理解していないと、無駄な処理が走るだけではなく、思わぬバグの原因となります。
次の例を見てください。

```csharp
public class Hoge
{
    public Hoge()
    {
        Fugas = Enumerable.Range(1, 10)
                          .Select(n => new Fuga());
    }

    public IEnumerable<Fuga> Fugas { get; }
}
```

この例では、`IEnumerable<Fuga>`を外部に公開しています。
もうお気づきとは思いますが、外部から`Fugas`プロパティが読まれるたびに、`Fuga`インスタンスが新しく10個生成されます。

これが意図した動作ならば良いのですが、「最初に生成した10個の`Fuga`インスタンスを使いまわしたい」という場合はこのコードではうまく動かないことになります。

##即時評価

`IEnumerable`の遅延評価の紹介を行いましたが、それと対極となる言葉が「__即時評価__」です。

「遅延評価」は「__必要になるときまで評価しない__」という考え方でしたが、
「即時評価」の場合はその名の通り「__即座に評価する__」となります。

遅延評価のように、「本当に必要になるときまで演算を実行しない」のではなく、「__将来必要になるかどうかは分からないけど、とりあえずすべて計算して、計算した結果をキャッシュしておく__」というのが即時評価の特徴です。
事前にすべての計算を完了させ、その計算結果をキャッシュするので、前述のような「無駄な処理が走る」「意図せずインスタンスが大量生成されてしまう」などといった遅延評価の懸念点は、即時評価に変更することで解決することができます。

即時評価するには、`List<T>`や`T[]`などの具象オブジェクトに変換する作業が必要になります。
とは言っても、`ToList()`や`ToArray()`などの拡張メソッドが用意されているため、LINQメソッドチェーンの最後にこれらを付けるだけで、即時評価に変更することができます。

```csharp
IEnumerable<int> enumerable =
    Enumerable.Range(1, 10)
              .Where(n => n % 2 == 0)
              .Select(n => n * 2)
              .ToList(); //即時評価。即命令が実行されenumerableには計算結果が入る

foreach (var num in enumerable)
{ 
    Console.WriteLine(num);
}
foreach (var num in enumerable)
{ //評価済みなので2回foreachしても無駄な処理は走らない
    Console.WriteLine(num);
}
```

このように、遅延評価によって引き起こされる懸念を解消するためには、即時評価を適度に利用することが必要になります。

ここまでが`IEnumerable`の遅延評価のお話でした。
続いて、`IObservable`のCold-Hotのお話です。

#`IObservable`

みんな大好きRx。Rxといえば`IObservable<T>`。
しかし、一口に`IObservable<T>`といっても「__Cold__」と「__Hot__」があります。
ちょうど、__`IEnumerable<T>`にも「遅延評価」と「即時評価」があるように__。

##HotとCold

まずは簡単にHotとColdの説明をします。

###Hot

Hotな`IObservable`とは、__購読者（Observer）を複数もつことができる`IObservable`のこと__を言います。

Hotな`IObservable`は、`Subscribe`されたときに、その購読者を購読者リストに保持します。
そして、値を発行するときには、購読者リストにあるすべての購読者に対して、一斉に値を発行します。

Observable（発行者）とObserver（購読者）が__1:多__の関係になるような`IObservable`と言えます。



###Cold

対してColdな`IObservable`とは、__購読者（Observer）を1つしか持つことができない`IObservable`__を指します。
Coldな`IObservable`は、購読者リストを保持する機能がなく、ただ一つの購読者しか管理することができないのが特徴です。

Observable（発行者）とObserver（購読者）が__1:1__の関係になるような`IObservable`と言えます。

###HotとColdの見分け方

HotとColdの見分け方は、__値が`Subject<T>`クラスから送出されているかどうか__で判断することができます。

Hotな`IObservable`は複数の購読者を管理することができると書きましたが、`Subject<T>`クラスはまさにこの、複数の購読者（Observer）を保持し、すべての購読者に対して同時に値を発行することができる機能を持っています。
__Hotな`IObservable<T>`とは、実質的には`Subject<T>`クラスそのものを指す__のです。
したがって、__自分で`Subject<T>`クラスを用意するなどして公開した`IObservable`は、Hotである__と判断することができます。

対して、Rxに用意されている各種オペレータやファクトリメソッドの多くは、`Subject<T>`クラスを利用していません。
そのため、後述する一部を除き、__Rxのオペレータやファクトリメソッドから生成された`IObservable`はColdである__と判断することができます。

##Coldな`IObservable`

前述したように、Coldな`IObservable`とは、__購読者（Observer）を1つしか持てないような`IObservable`__です。
値の発行者（Observable）と値の購読者（Observer）が__1対1の関係__になるような`IObservable`とも言えます。

例えば、以下のコードを見てください。

```csharp
//変数「observable」に1〜10までの値を発行するIObservableを代入
IObservable<int> observable = 
    Observable.Range(1, 10);

//observableを購読
observable.Subscribe(Console.WriteLine);
```

`Observable.Range`ファクトリメソッドから生成される`IObservable`はColdです。
Coldなので、__購読者は1つしか持てません__。
この場合、購読(`Subscribe`)しているのは1つのみのため、何も問題なく購読することができます。

では、次のように__複数の購読者__がいた場合はどうなるのでしょうか。

```csharp
//変数「observable」に1〜10までの値を発行するIObservableを代入
IObservable<int> observable = 
    Observable.Range(1, 10);

//observableを複数の購読者が購読
observable.Subscribe(Console.WriteLine);
observable.Subscribe(Console.WriteLine);
```

Coldな`IObservable`なのに複数回`Subscribe`されています。
しかしながら、この場合でも、Coldだから1回しか`Subscribe`を受け付けないということはなく、きちんと`Subscribe`した分だけ購読することができます。

Coldな`IObservable`は1つしか購読者が持てないのに、複数回`Subscribe`ができるのは矛盾しているように感じます。
なぜ、Coldな`IObservable`なのに複数の購読者を登録できるのかというと、__Observableソースが`Subscribe`された分だけ生成されている__からです。
結果的には、__Observableソースと購読者は1対1__となるため、Coldな`IObservable`の要件は満たすというわけです。




###Coldな`IObservable`の注意点

`Subscribe`した分だけObservableソースが生成されるとはいえ、Coldな`IObservable`でも複数購読が可能であれば、何が問題なのか？と思うかもしれませんが、次のような場合に注意が必要になってきます。

```csharp

IObservable<int> observable =       //変数「observable」に、
    Observable.Range(1, 10)         //「1~10までの整数を発行するObservableソースを生成して
              .Where(n => n % 2)    //そのうち偶数のものだけを次に通し、
              .Select(n => n * 2);  //2倍にしたものを発行する命令」を格納

//observableを複数の購読者が購読
observable.Subscribe(Console.WriteLine);
observable.Subscribe(Console.WriteLine);
```

このコードでは、「1~10までの整数を発行するObservableソースを生成して、そのソースから発行された値を偶数で絞り込んで、絞り込んだ結果に対して2倍したものを発行する命令」を変数`observable`に代入しています。
そして、その`observable`を複数回`Subscribe`しています。

変数`observable`には、「1~10までの整数を発行するObservableソースを生成して、そのソースから発行された値を偶数で絞り込んで、絞り込んだ結果に対して2倍したものを発行する命令」が入っているのでした。
これを複数回`Subscribe`すれば、「1~10までの整数を発行して、それを偶数で絞り込んで、その絞り込み結果を2倍したものを発行する`IObservable`」が複数個複製されてしまうことになります。

つまり、__Observableソースの生成処理とLINQオペレータの処理が、Subscribeされた回数分だけ走る__ことになってしまいます。

これが意図した動作ならば良いのですが、そうでない場合（1個のObservableソースを共有したい場合）には、無駄な処理が走ってしまったり、場合によってはバグの原因となってしまうため、注意が必要です。



##Hotな`IObservable`

Coldの対極となるのがHotです。

Coldな`IObservable`が1つの購読者しか持てないのに対して、
Hotな`IObservable`は__複数の購読者を同時に持つことができます__。

つまり、Hotな`IObservable`ならば、__複数回`Subscribe`されても、Observableソースを複製することなく__、各購読者に対して値を発行することができます。
したがって、前述したようなColdな`IObservable`の問題点は、__Hotな`IObservable`に変換することで解決する__ということになります。

幸い、RxにはColdをHotに変換するオペレータが用意されているため、Hot変換したいタイミングで簡単にHot変換することができます。
__その代表例が、`Publish()`メソッドです。__

`Publish()`メソッドの役割を簡単に説明すると、次のようになります。

1. 入力となる`IObservable<T>`と`Subject<T>`を内部に保持する。
2. `Connect()`メソッドが呼ばれると、入力の`IObservable`を`Subscribe`して、得た通知をそのまま`Subject<T>`に伝達する。
3. `Subject<T>`の`IObservable<T>`を外部に公開する。

つまり、入力となるColdな`IObservable`から発せられる通知を、__複数の購読者を管理する機能を持つ`Subject<T>`に仲介させる__ことによって、複数の購読者を受付可能なHotな`Observable`に変換するという仕組みとなっています。

また、`Connect()`メソッドを呼び出す必要がありますので、`Publish()`メソッドの戻り値は__`IConnectableObservable`インターフェイス__となります。

使用例としては次のようになります。

```csharp

IConnectableObservable<int> observable = 
    Observable.Range(1, 10)
              .Where(n => n % 2)
              .Select(n => n * 2)
              .Publish(); //Publish以前のIObservableを保持したIConnectableObservableにする

//Publishの内部が持つSubjectを購読
observable.Subscribe(Console.WriteLine);
observable.Subscribe(Console.WriteLine);

//Publish以前のIObservableをSubscribeして、Subjectに伝える
observable.Connect();

```

詳しい使い方については他記事で紹介されているため、そちらを参照ください。

https://qiita.com/acple@github/items/8d3a4d3414fa59adff70#hotなobservablecoldなobservable

#`IEnumerable`の遅延評価と`IObservable`のHotColdの関係性

前置きが非常に長くなりここからが本題ですが、「`IEnumerable`の遅延評価・即時評価」と「`IObservable`のHot・Cold」って、似ているものがある思いませんか？
違いを以下の表にまとめてみました。

|  | `IEnumerable` | `IEnumerable` | `IObservable` | `IObservable` |
| --- | --- | --- | --- | --- |
|  | __遅延評価__ | __即時評価__ | __Cold__ | __Hot__ |
| 使用する命令 | `foreach` | `foreach` | `Subscribe` | `Subscribe` |
|変数に入っているもの|コレクション__操作の命令__が入っている|コレクションの操作済みの__値そのもの__が入っている|`IObservable`からの通知結果を__操作する命令__が入っている|__Observableソースそのもの__が入っている
| 特徴 | 複数回`foreach`されると__命令が毎回実行__される | 複数回`foreach`されても__命令の実行は1回__だけ | __単一__の購読者しか持てない。<br>複数回`Subscribe`されるとObservableソースから複製される | __複数__の購読者を持てる |
| 変換 | __即時評価に変換__：<br>`ToList()`, `ToArray()`など | - | __Hotに変換__：<br>`Publish()`, `Multicast()`など | - |

##変数に「命令」が入っているか「そのもの」が入っているか

１つ目の共通点として、変数に入っているものの特性が挙げられます。

###「遅延評価」と「Cold」は「命令」が入っている

表からもわかるように、「IEnumerableの遅延評価」と「IObservableのCold」は、どちらも変数に「__命令__」が入っているということがわかります。

ここでいう「命令」というのは、具体的にはLINQオペレータによる操作のことです。
変数に「命令」、すなわちLINQオペレータによる操作が入っているということは、複数回使用するとLINQオペレータによる操作がその分毎回実行されることを意味します。

すでに挙げた例ですが、以下のような例で考えます。

```csharp

IEnumerable<int> enumerable =
    Enumerable.Range(1, 10)
              .Where(n => n % 2 == 0)
              .Select(n => n * 2);
```

このとき、遅延評価`IEnumerable<int>`型の変数「enumerable」には、1〜10までの整数のコレクションを作って、偶数で絞り込んで、2倍して、という「__命令__」が入ります。
「命令」が入っているので、複数回使用すれば、当然「命令」も複数回実行されてしまいます。


Cold Observableの場合も同様です。

```csharp
IObservable<int> observable =
    Observable.Range(1, 10)
              .Where(n => n % 2 == 0)
              .Select(n => n * 2);
```

Coldな`IObservable<int>`型の変数「observable」には、1〜10までの整数を発行するObservableソースを作って、偶数で絞り込んで、2倍して、という「__命令__」が入ります。
「命令」が入っているので、複数回購読すれば、当然「命令」も複数回実行されます。


###「即時評価」と「Hot」は「そのもの」が入っている

「そのもの」とは、その変数から最終的に得られる情報のことです。
`IEnumerable`であれば、Enumerableソースから情報を受け取ってLINQによる演算をした結果そのものであるし、
`IObservable`であれば、Observableソースからの通知を受けてLINQによる演算を行った結果に対する購読権のことです。

最終的に得られる情報そのものが入っているので、複数回`foreach`しようが、複数回`Subscribe`しようが、LINQによる操作は重複して実行されることはありません。
最初の１回だけ実行され、あとは、計算済みの値を使い回すことができます。

##「命令」から「そのもの」への変換は可能、ただし逆は不可能
２つ目の共通点として、「命令」から「そのもの」への変換は可能だが、逆は不可能という点があります。

`IEnumerable`ならば`ToArray()`や`ToList()`、
`IObservable`ならば`Publish()`や`Multicast()`といった、「命令」から「そのもの」への変換メソッドが用意されています。
どちらも、LINQメソッドチェーンの末尾に付けるだけで、簡単に変換することが可能です。

ただし、逆は不可能です。
一度「そのもの」、つまり演算結果になってしまったものを、「命令」、つまり演算する方法に戻すことはできないからです。

##つまり何が言いたいのか

- コレクション版LINQもRx版LINQも、ひとつのソースを複数で共有して使うことがあったら、遅延評価とColdには気をつけましょう。
- 遅延評価やColdのまま複数箇所で共有して使用すると、ソースまで遡ってLINQ演算命令が複製されてしまいます。
- 回避するには即時評価・Hot変換を行いましょう。


#最後に


「`IEnumerable`の遅延評価・即時評価」と「`IObservable`のCold・Hot」は関係が薄いように見えても、本質的な部分を見ると意外と関係があるのではないか？と思えてきたので記事にして投稿してみました。

やはり他人に説明できるように記事にすると、理解が曖昧だった部分を強制的に勉強することになるので理解が深まって良いですね。
異論やご指摘はもちろん受け付けますので、是非コメントお寄せください。

最後までありがというございました。