More Related Content Similar to An Internal of LINQ to Objects Similar to An Internal of LINQ to Objects (20) More from Yoshifumi Kawai More from Yoshifumi Kawai (20) An Internal of LINQ to Objects1. An Internal of LINQ to Objects
2013/12/14
Yoshifumi Kawai - @neuecc
2. Self Introduction
@仕事
株式会社グラニ 取締役CTO
C# 5.0 + .NET Framework 4.5 + ASP.NET MVC 5
最先端C#によるハイパフォーマンスWebアプリケーション
@個人活動
Microsoft MVP for Visual C#
Web http://neue.cc/
Twitter @neuecc
LINQがひじょーに好き、趣味はライブラリ作成
4. Session Target
LINQ to Objectsを十分知っている人
知らない状態だと全体的にイミフかも?
LINQ to Objectsという観点ではLEVEL 500を予定は未定
もっと極めて見たいと思う人向け
必ずどれかは知らなかった!というネタがあるはず
もちろん、それ知ってるよ!もあるはず
使えないネタから使えないネタまで
基本的には使えないネタ多め、但し一部は非常に役立つかとは
6. new Enumerable<T>()
// メソッドチェーン
Enumerable.Range(1, 10).Select(i => i * i).Take(5);
// を、細分化すると?
var rangeEnumerable = Enumerable.Range(1, 10);
var selectEnumerable = rangeEnumerable.Select(i => i * i);
var takeEnumerable = selectEnumerable.Take(5);
takeEnumerable.GetEnumerator();
9. Immediate vs Deferred
即時実行(Immediate Execution)
クエリ演算子を呼ぶとすぐに実行されるもの
ToArray, First, Sum, etc...
ようするにIEnumerable<T>ではなく具体的な何かを返すもの
遅延実行(Deferred Execution)
クエリ演算子を呼んだ時点では実行されないもの
Where, Select, OrderBy, etc...
ようするにIEnumerable<T>を返すもの
メソッドチェーンしても毎回具体的な何かを返さないから実行効率
やメモリ効率が良い、よって好きに繋げてください
10. Streaming vs Non-Streaming
遅延実行は、更に2つのカテゴリーに分けられる
Streaming
必要なだけ少しずつ読み取っていく
Where, Select, Concat, Union, Distinct, Take, etc...
Non-Streaming
実行が開始された瞬間に全てのデータを読み込む
OrderBy/Descending, ThenBy/Descending, GroupBy, Reverse
Join, GroupJoin, Except, Intersectは2つ目のシーケンス側が
16. イテレータはいつ開始されるか
static IEnumerable<int> Iter()
{
Console.WriteLine("IterStart");
yield return 1;
Console.WriteLine("IterEnd");
}
static void Main(string[] args)
{
Console.WriteLine("Before GetEnumerator");
var e = Iter().GetEnumerator();
Console.WriteLine("After GetEnumerator");
Console.WriteLine("Before MoveNext1");
e.MoveNext();
Console.WriteLine("After MoveNext1");
Console.WriteLine("Current:" + e.Current);
Console.WriteLine("Before MoveNext2");
e.MoveNext();
Console.WriteLine("After MoveNext2");
}
17. イテレータはいつ開始されるか
static IEnumerable<int> Iter()
{
Console.WriteLine("IterStart");
yield return 1;
Console.WriteLine("IterEnd");
}
Before GetEnumerator
static void Main(string[] args)
{
Console.WriteLine("Before GetEnumerator");
var e = Iter().GetEnumerator();
Console.WriteLine("After GetEnumerator");
Console.WriteLine("Before MoveNext1");
e.MoveNext();
Console.WriteLine("After MoveNext1");
Console.WriteLine("Current:" + e.Current);
Console.WriteLine("Before MoveNext2");
e.MoveNext();
Console.WriteLine("After MoveNext2");
}
After MoveNext1
Current:1
Before MoveNext2
After GetEnumerator
Before MoveNext1
IterStart
IterEnd
After MoveNext2
最初のMoveNextが呼ばれたタイ
ミングで動き出す
19. 引数チェックのタイミング
static IEnumerable<string> StringRepeat(string str, int count)
{
// 引数チェックしてるのに
if (str == null) throw new ArgumentNullException();
for (int i = 0; i < count; i++)
{
yield return str;
}
最初のMoveNextが呼ばれるまで
本体は動き出さない
}
static void Main(string[] args)
{
// 何も起こりません
var repeat = StringRepeat(null, 100).Take(10);
}
MoveNextが呼ばれる = foreach or 即時
実行のLINQクエリ演算子を呼んだ時
20. 分離するというマナー
public static IEnumerable<string> StringRepeat(string str, int count)
{
// 引数チェックは先に行って
if (str == null) throw new ArgumentNullException();
return StringRepeatCore(str, count);
メソッドを2つに分割する
}
// 本体はprivateに分離
private static IEnumerable<string> StringRepeatCore(string str, int count)
{
for (int i = 0; i < count; i++)
{
yield return str;
}
全てのLINQ標準クエリ演算子がこれに
}
従っています。自分でyield return使って
実装する時も従うべき
22. Stable Sort and Unstable Sort
大雑把なソートの分類
破壊的か、非破壊的か
OrderByはIEnumerable<T>が元で破壊不能というのもあり非破壊的
安定ソートか、非安定ソートか
安定ソートは同じ値が来た時、ソート前の順序が維持される
OrderByのアルゴリズムはQuickSort
QuickSortは非安定ソート
OrderByは安定ソート
ん?あれ?
25. OrderBy as Shuffle, OrderBy as MaxBy
Shuffleが欲しい?
seq.OrderBy(_ => Guid.NewGuid())
Sortの比較関数にランダムは危険で偏りが出てしまう
だけどOrderByの仕組みの場合は比較関数ではないので大丈夫
MaxBy, MinByが欲しい?
seq.OrderBy(x => x.Age).First();
seq.OrderByDescending(x => x.Age).First();
さすがOrderBy、なんでもできる!そこにシビれる憧れれぅ!
30. 複数キーによるグルーピング
匿名型を使おう!
seq.GroupBy(x => new { x.Age, x.Name });
匿名型はGetHashCodeとEqualsを全プロパティで比較して一致させ
るので、判定要素に使える
裏にあるDictionaryを考える
集合系やグルーピングなど、IEqualityComparerを受けるオーバー
ロードがあるものは裏でHashSetやDictionaryが暗躍している
要素の等しさを判定するのにIEqualityComparerを使うわけです
35. Transparent Identifier #from
from a in source
from b in source
from c in source
from d in source
from e in source
where a % 2 == 0 && b % 2 == 0 && c % 2 == 0
select string.Concat(a, b, c, d, e);
37. Language INtegreated Monad
static void Main(string[] args)
{
// クエリ構文でusingを表現
var firstLines =
from path in new[] { "foo.txt", "bar.txt" }
from stream in File.OpenRead(path) // FileStreamがSelectManyの対象に
from reader in new StreamReader(stream) // StreamReaderがSelectManyの対象に
select path + "¥t" + reader.ReadLine();
}
public static IEnumerable<TResult> SelectMany<TSource, TDisposable, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TDisposable> disposableSelector,
Func<TSource, TDisposable, TResult> resultSelector) where TDisposable : IDisposable
{
foreach (var item in source)
{
using (var disposableItem = disposableSelector(item))
Select, Where, SelectManyなどは
{
名前が一致すれば、自作メソッド
yield return resultSelector(item, disposableItem);
}
をクエリ構文で使うことが可能
}
}
40. それAny
if(seq.Count() != 0) vs if(seq.Any())
Countは最初から最後まで全て列挙して個数を計算する
Anyは最初の一件があるかないかだけをチェックする
Anyったらさいきょーね
ま、こういうチェックってその後に別のメソッド
を、呼んでたら2回列挙ですね?
→前の方のMaterializeの話
ToArrayしますか、しましょうか
42. There, There, Where, Where
連打 is 楽しい
.Where(x => pred1(x))
.Where(x => pred2(x))
.Where(x => pred3(x))
それ&&?
.Where(x => pred1(x) && pred2(x) && pred3(x))
パイプライン削減?効率厨め!
43. LINQ has Composability
LINQの合成可能性を活かすとき
var flag = true;
var seq = Enumerable.Range(1, 10).Where(x => x % 2 == 0);
if (flag)
{
seq = seq.Where(x => x % 3 == 0).Take(2);
}
&&に常にまとめられるとは限らない
Funcも合成できるけどC#で関数合成は面倒だし
または、.Where(predicate)が意味でまとまってる時
Where連打したほうがわかりやすいじゃない?
48. 空シーケンスと集計
例外出ます。
Aggregateはseedつければ回避可能
var empty = Enumerable.Empty<int>();
// InvalidOperationException
var product1 = empty.Aggregate((x, y) => x * y);
// 0
var product2 = empty.Aggregate(0, (x, y) => x * y);
// InvalidOperationException
var max = empty.Max();
// InvalidOperationException
var min = empty.Min();
// InvalidOperationException
var avg = empty.Average();
Sumは0
// 0
var sum = empty.Sum();
49. 空シーケンスとNullable(or class)
例外出ません。
// int?の空シーケンス
var empty = Enumerable.Empty<int?>();
null返します。
// InvalidOperationException
var product = empty.Aggregate((x, y) => x * y);
さすがにAggregateはダメ
この振る舞いが妥当なのか
は諸説あるとかないとか
極力、値を返すようにする、という観
点で見ればstructはデフォルト値がな
いからダメでclassならあるので返せる
から、と見たり
// null
var max = empty.Max();
// null
var min = empty.Min();
// null
var avg = empty.Average();
50. DefaultIfEmpty #割と忘れる
// Minが欲しいけどWhereでフィルタした結果Emptyになってしまう
// けど例外は避けたい!
// わざわざToArrayしてAnyで確認してから、とかも嫌!
var min = Enumerable.Range(1, 10)
.Where(x => x > 10)
.Cast<int?>() // Nullableにキャストして
.Min() ?? -1; // nullのままが嫌なら??で値を
// 素直にDefaultIfEmpty使いましょう:)
var min = Enumerable.Range(1, 10)
.Where(x => x > 10)
.DefaultIfEmpty(-1)
.Min();
54. LINQ to Objects for JavaScript
JavaScript上で完全完璧に再現
ICollectionなどの最適化 => 入ってる
Where.Whereなどの最適化 => 入ってる
OrderByなどの仕組み => 入ってる
標準クエリ演算子を全収録+α
プラスアルファでむしろ二倍以上のメソッド数
JavaScriptのための
文字列ラムダ式, TypeScript定義, IntelliSense用vsdoc