導入
foreachで回せるものは、すべてIEnumerable<T>という「列挙できる」共通の形をしています。じつは自分でも、yield returnを使えば1件ずつ値を生成する列を簡単に作れます。これをイテレータと呼び、LINQの遅延実行(必要になるまで計算しない)の正体でもあります。
図解
flowchart LR
F["foreach が1件求める"] --> Y["yield return で1件返す"]
Y --> P["続きの状態を覚えたまま一時停止"]
P -->|次を求められたら| Y
Y -.->|必要な分だけ生成| L["遅延評価(無限列もOK)"]
サンプル
// yield return で「1件ずつ生成する」独自の列を作れる
IEnumerable<int> Squares(int n)
{
for (int i = 1; i <= n; i++)
{
yield return i * i; // ここで1件返し、次に呼ばれたら続きから再開する
}
}
foreach (int sq in Squares(4))
Console.Write($"{sq} "); // 1 4 9 16
Console.WriteLine();
// 遅延評価: 無限に続く列でも、必要な分だけ取り出せる
IEnumerable<int> Naturals()
{
int n = 1;
while (true) yield return n++; // 無限ループだが…
}
foreach (int x in Naturals().Take(3)) // Take(3) で先頭3件だけ取り出す
Console.Write($"{x} "); // 1 2 3
IEnumerable<T>は「foreachで1件ずつ取り出せる」共通の型yield returnで値を返すと、メソッドは状態を覚えたまま一時停止し、次に呼ばれたら続きから再開する- 必要な分だけ生成する遅延評価なので、無限列でも
Takeなどと組み合わせて安全に扱える
演習
// TODO: yield return を使って 3, 2, 1 を順に返す CountDown() を定義し、
// foreach で空白区切り出力してください("3 2 1 ")
IEnumerable<int> CountDown()
{
___
}
foreach (int n in CountDown())
Console.Write($"{n} ");
- 期待される出力:
3 2 1
ヒント1を見る
for (int i = 3; i >= 1; i--) yield return i;
ヒント2を見る
CountDown の中身を for (int i = 3; i >= 1; i--) { yield return i; } にします
まとめ
foreachで回せるものの正体はIEnumerable<T>yield returnで「1件ずつ生成する列(イテレータ)」を自作できる- 必要な分だけ計算する遅延評価は、LINQの遅延実行と同じ仕組み
次回: ここまでの入れ物を「どう選ぶか」を整理します。