イテレーターブロックを理解する

前回の投稿[拡張メソッドを使って自力で、Map を実装する](http://qiita.com/TsuyoshiUshio@github/items/0c9514d189e3ebd5223d)は、今回のイテレーターブロックを理解するための、ステップなのです（自分にとって。）

早速イテレーターブロックに関して理解していきましょう。

# イテレーターブロックとは

イテレータブロックとは、`yield return` や `yield break` を含むブロックのことです。特に前回紹介したような、イテレーターをゴネゴネしたいときにとても有効な書き方です。

前回のおさらいをしてみましょう。オレオレ Map をこんな感じで実装しました。

```csharp
    static class EmployeeExtensions
    {
        public static IEnumerable<X> Map<T, X>(this IEnumerable<T> e, Func<T,X> f)
        {
            List<X> x = new List<X>();
            foreach(var element in e)
            {
                x.Add(f(element));
            }
            return x;
        }
    }
```

これだけで、次のようなコードをかくと、IEnumerable を実装したクラスが拡張されます。

```csharp
            var list = new List<int> { 1, 2, 3, 4 };
            var result = list.Map<int, int>(p => p + 1);
            foreach (var e in result)
            {
                Console.WriteLine(e);
            }
            Console.ReadLine();
```
結果

```
2
3
4
5
```

Map を実装してカッケーという感じですが、さらにおしゃれにかけます。今までは、Map の中で、List を作ってそこに、関数適用した結果を渡して、戻り値を作成していましたが、イテレーターブロックを使うとこんなに簡潔にかけます。

```csharp
        public static IEnumerable<X> CoolMap<T, X>(this IEnumerable<T> e, Func<T,X> f)
        {
            foreach(var element in e)
            {
                yield return f(element);
            }
        }
```

おしゃれすぎる、、、つまり、yield return が呼ばれるたびに `f(element)` が イテレーターによって返却される感じです。じゃあ、それをつかったコードを書いてみますが、せっかくなのでちょっと変えてみましょう。

```csharp
            var strings = new List<string> { "a", "b", "c" };
            var upper = strings.CoolMap<string, string>(p => p.ToUpper());
            foreach(var e in upper)
            {
                Console.WriteLine(e);
            }
            Console.ReadLine();
```

実行結果。ううむいい感じ。ジェネリクス様様。

```csharp
A
B
C
```

# でたらめな結果を返してみる。

さて、`yield return` の理解を深めるために、自分で自らでたらめな値を返却してみましょう。

```csharp
        public static IEnumerable<int> Random(this IEnumerable<int> e)
        {
            yield return 8;
            yield return 6;
            yield return 10;
            yield return 19;
            yield break;
            yield return 20;
        }
``` 

最初のリストなど関係ない。といったコードになっています。実行しましょう。

```csharp
            var dummy = new List<int> { 1, 2, 3, 4 };
            foreach (var e in dummy.Random())
            {
                Console.WriteLine(e);
            }
            Console.ReadLine();
```

予想通り、リストで定義された `1, 2, 3, 4` は無視され、`yield return` で指定した値が一つづつ返却されています。そして、`yield break;` が来た時点でイテレーターは終了します。ですので、`20` はスルーされています。 

```
8
6
10
19
```

# 遅延実行

さて、この仕組みを理解したかったのは、師匠が、Linq では遅延実行がつかわれているという話でそのサンプルを書いてくれたからです。[師匠サンプル](https://gist.github.com/runceel/e798e0082777d7e9e2b7479fd428a3f1)

ここで使われているテクニックが理解できなかったため２つのブログを書いて理解しようとしたわけです。今はりかいできます。もともと私が持っていた疑問は次のものです。

```csharp
var result = new T();
properties.Select(p => { p.SetValue(result, Environment.GetEnvironmentVariable(p.Name));
  e = Environment.GetEnvironmentVariable(p.Name);
  Console.WriteLine("Debug!:" + e);
  return p; });
return result;
```

こういうコードを書いたのですが、うまく動かなかったのです。関数を適用したかったわけではなく、foreach ではなく、Linq でワンライナーでループできないかなと思いました。これは、リフレクションで、取ってきたメソッド名と同名の環境変数の値をセットしようとしていました。Linq は遅延評価らしいです。
　しかし、これはうまくいきません。この書き方だと、この関数が実行されることはありません。なぜなら、`properties.Select` の戻り値が使われるときにはじめて、この関数が適用されるからです。この書き方だと、`properties.Select` の戻り値が捨てられているので、この関数適用が有効になることはありません。

イテレーターブロックで書くと、遅延評価になるようなのでコメントを入れて違いを確かめてみましょう。
イテレータブロックを使っていない`Map` と、イテレーターブロックを使った `CoolMap` を使ってコードを書いてみました。

```csharp
            var somelist = new List<int> { 1, 2, 3 };

            Console.WriteLine("Start! Map!");
            var justnowresult = somelist.Map<int, int>(p => {
                Console.WriteLine("Doing!"); // Doing! の位置で評価タイミングがわかる！
                return p + 1;
            });
            Console.WriteLine("Ready Go!");
            foreach (var i in justnowresult)
            {
                Console.WriteLine(i);
            }
            Console.WriteLine("Finish!");


            Console.WriteLine("Start! CoolMap!");
            var lazyresult = somelist.CoolMap<int, int>(p => {
                Console.WriteLine("Doing!");　// Doing! の位置で評価タイミングがわかる！違いに注意
                return p + 1;
            });
            Console.WriteLine("Ready Go!");
            foreach (var i in lazyresult)
            {
                Console.WriteLine(i);
            }
            Console.WriteLine("Finish!");
            Console.ReadLine();
```

実行結果を見てみましょう。`Map`に渡された関数は、Map適用時に既に評価されているの対して、`CoolMap`に渡された関数は、`foreach`のタイミングで評価されているのがわかります。

```
Start! Map!
Doing!
Doing!
Doing!
Ready Go!
2
3
4
Finish!
Start! CoolMap!
Ready Go!
Doing!
2
Doing!
3
Doing!
4
Finish!
```

というわけでやっとすっきりしました。Linq の Select はこんなイメージに似ていて遅延評価なんですね。また一つ学びましたし、イテレータブロックも学んだので、ライブラリ作るときに役立ちそうです！

# 参考

* [イテレータ](http://ufcpp.net/study/csharp/sp2_iterator.html)
* [師匠サンプル](https://gist.github.com/runceel/e798e0082777d7e9e2b7479fd428a3f1)
* [拡張メソッドを使って自力で、Map を実装する](http://qiita.com/TsuyoshiUshio@github/items/0c9514d189e3ebd5223d)