C#のiEnumerable#SelectはEnumerator::Lazy#mapだった件
C#は.net framework 3.5より、LINQ用のEnumerableメソッドが追加されています。
Select,Where,OrderByなどです。
これらのメソッドは、LINQでの使用はもちろん、ラムダ式を渡して、利用することができます。これは、Rubyのmapやfilterに当たるものになります。
参考:http://www.atmarkit.co.jp/ait/articles/1107/22/news141.html
ここで、iEnumerable#Selectのザックリとした実装を見てみますと、以下のようになっています。
public static IEnumerable<TResult> Select<TSource, TResult>( this IEnumerable<TSource> source, Func<TSource, TResult> block) { foreach (TSource e in source) yield return block(e); }
ここで、問題になるのが、yield return block(e);の部分になります。この構文は、実際にその要素が利用されるまで、blockを実行しません。すなわち、遅延評価となっています。LINQの実装から考えると当然ですね。
参考:http://ufcpp.net/study/csharp/sp3_lazylist.html
ですので、次のようなコードを書くとハマります。
int y = 0; var l = new list<int>{1,2,3}.Select((x) => y += x); Console.WriteLine(y);//y= 0 (6になると思いきや、まだ評価されていない) l.Last();//コレで全部評価される。 Console.WriteLine(y);//y= 6
そういえば、これはどこかで聞いたことのある話のような...。あー、Enumerator::Lazyですね。分かります。
参考:http://magazine.rubyist.net/?0041-200Special-lazy
ということで、Rubyのmap,filter相当のメソッドは実は存在しませんので、使いたいのであれば、自分で定義するしかないようです。こんな感じかな。
public static IEnumerable<TResult> Map<TSource, TResult>( this IEnumerable<TSource> source, Func<TSource, TResult> block) { List<TResult> l = new List<TResult>(); foreach (TSource e in source) l.Add( block( e ) ); return l; } public static IEnumerable<TSource> Filter<TSource>( IEnumerable<TSource> source, Func<TSource, bool> predicate) { List<TSource> l = new List<TSource>(); foreach (var item in source) { if (predicate(item) ) { l.Add(item); } } return l; }