遅延評価
LINQではC#2.0の新機能であるイテレータ(yield)が使われています。イテレータは発表初期にforeachが簡単に書けると言った紹介のされ方だったので、その真価が伝わっていない気がします。例えば、LINQでも使われている遅延評価とか。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Query;
// やっつけでこさえたIEnumerator/IEnumerable
// Currentにアクセスしたらコンソール出力させたかっただけ
class MyEnumerator<T> : IEnumerator<T>, IEnumerable<T> {
T arr;
int cur;
public MyEnumerator (T arr) {
this.arr = arr;
Reset ();
}
public void Dispose () {}
public IEnumerator<T> GetEnumerator () {
return new MyEnumerator<T> (arr);
}
IEnumerator IEnumerable.GetEnumerator () {
return GetEnumerator ();
}
public void Reset () {
cur = -1;
}
public bool MoveNext () {
if (cur >= arr.Length)
return false;
return ++cur < arr.Length;
}
public T Current {
get {
Console.Write ("{0},", arr [cur]);
return arr [cur];
}
}
object IEnumerator.Current {
get {
return Current;
}
}
}
static class Program {
// Sequence.Whereとの比較用メソッド
static IEnumerable<T> MyWhere<T> (IEnumerable<T> e, Predicate<T> pred) {
List<T> list = new List<T> ();
foreach (T t in e)
// predを満たす要素のみlistに格納する
if (pred (t))
list.Add (t);
return list;
}
static void Main () {
var nums = new [] { 1, 3, 5, 7, 9, 8, 6, 4, 2, 0 };
var e = new MyEnumerator<int> (nums);
Console.WriteLine ("# MyWhere");
var nums1 = MyWhere<int> (e, x => x % 2 == 0);
Console.WriteLine ();
// Sequence.Whereは遅延評価される
Console.WriteLine ("# Where");
// num2は状態遷移マシンが返されるだけ
// MyWhereと違ってコンソールには出力されない
var num2 = Sequence.Where (e, x => x % 2 == 0);
Console.WriteLine ();
// ここで評価される
Console.WriteLine ("# foreach");
foreach (var n in num2)
Console.WriteLine ("*{0}*", n);
}
}
/* 結果
# MyWhere
1,3,5,7,9,8,6,4,2,0,
# Where
# foreach
1,3,5,7,9,8,*8*
6,*6*
4,*4*
2,*2*
0,*0*
*/
イテレーター無しでLINQ(というかSystem.Query)を実装したら、MyWhereみたいにメモリ無駄食いで使い物になりませんよね。(^^;