Contenu connexe Similaire à An Internal of LINQ to Objects (20) Plus de 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