連載:C# 3.0入門第6回 LINQ基礎編株式会社ピーデー 川俣 晶2008/08/29 |
|
|
シンプルなソート
foreach文を使った単なる列挙に対するLINQの長所の1つは、簡単にソートができることだろう。orderby句をクエリ式に追加するだけである。
以下は、与えられた数値のマイナス符号を無視し、値の大きい順に並べ替える例である。
| |
リスト11 絶対値の逆順ソート |
ここで、「orderby Math.Abs(x) descending」という部分がソートの指定に当たる。「Math.Abs(x)」はソートの根拠になる値を計算する式となる。「descending」は逆順(降順)を指定するキーワードで、昇順を指定するキーワードはascendingだが、これは省略できる。
さて、この実行結果を見ると、2よりも先に-2が来ている。Math.Abs(2)とMath.Abs(-2)はどちらも2になるので、どちらが先に来るかはそれだけでは思いどおりに制御できない(ただし、順序が保存された安定した並べ替えを行うので、結果は一定している)。
しかし、orderby句はソート条件を複数指定できるので、カンマ区切りでもう1つの条件を付加すれば、思いどおりにコントロールできる。-2よりも2を先にするには、絶対値ではない値の降順(x descending)を追加すればよい。
| |
リスト12 複数のソート条件を指定したクエリ |
この場合の実行結果は以下のようになる。
| |
リスト12の実行結果 |
クエリの接続
以下のようなクエリ式があったとしよう。このクエリ式は、1から10までの整数を2乗した値が50を超えるもののみを選び出す。
| |
リスト13 2乗すると50を超える数値のみを抽出 |
しかし、この式には「n * n」という計算が2回出てきて冗長である。この計算を1つにするには、例えば次のように「1から10までの整数を2乗した値」のシーケンスを得るクエリ式と、値が50を超えるもののみを選び出すクエリ式を分離すればよい。
| |
リスト14 2つのクエリ式に分離 |
これで「n * n」は1つになった。
だが、これはこれでクエリ式が2つあり、別の意味で冗長である。ここで、into句を使うと、1つのクエリの結果を別のクエリのソースとすることができる。つまり、2つのクエリ式をinto句で接続することで、これを1つのクエリ式にまとめることができる。
| |
リスト15 2つのクエリをinto句により接続 |
この例で、「from n in ……」がソースからの入力を範囲変数mで受けるのと同様に、「…… into m」は手前のクエリ式の結果を範囲変数mで受けている。それにより、m > 50という条件判断が可能となっている。
クエリ結果のグループ化
通常、クエリ式はselect句で終わり、これによって結果の内容を確定させる。しかし、このほかにgroup句を使い、結果を分類することができる。
以下の例は、名前とCPUのペアとなる情報を、CPUによってグループ化して分類する。つまり、CPUが「6800」のものだけ集め、「Z-80」のものだけ集め……という処理をすべて行うわけである。
| |
リスト16 group句によるグループ化 |
group句は、「group 要素 by キー」という書式で使用される。キーの値ごとにグループ化された要素のコレクションが得られる。
また、group句はselect句と異なり、クエリ結果の型を自由に指定できず、常に「IGrouping<TKey, TElement>」という型に固定される。これは結果が指定型のリストという形で提供されるためである。TKeyはキーの型、TElementは要素の型である。
そして、IGrouping<TKey, TElement>型の値を列挙すれば、1つのグループに含まれる要素を得ることができる。また、Keyプロパティを参照することで、キーの値を得ることができる。「CPU=Z-80」のような出力は、このKeyプロパティの値を参照することで実現している。
複数ソースを関連付けるjoin句
複数のソースの値を関連付けることは、複数のfrom句を使うことでも実現できる。例えば、以下のリスト17のようなクエリ式を書くことで、同じId番号を持つ商品情報オブジェクトと、商品販売価格オブジェクトを関連付けることができ、名前と価格を組み合わせたリストを出力することができる。
| |
リスト17 複数のソースを関連付ける |
しかし、このようなコードは効率が良くない。上の掛け算の九九表サンプルを見て分かるとおり、2つのソースについてすべて総当たりで調べることになるためだ。つまり、商品情報が3つ、商品販売価格が3つなら、3×3=9回の判定が実行される。
これを改善するには、関連付けという役割に特化したjoin句を使用するとよい。具体的には、クエリ式を以下のように書き直す。
| |
リスト18 join句への書き換え |
これは、join句の使い方の1つである「内部結合」を使用したものである。この場合、join句は、以下のような書式で記述する。
| |
join句の構文 |
ここで「join …… in ……」の部分は、「from …… in ……」の構文と似ている。しかし、決定的に違うのは「on」以降の構文が存在する点である。これは、2つのソースを関連付ける条件を指定するために存在する。
ここで注意すべき点は、from句の場合、関連付けるソースを選ぶ条件として、「where x.Id == y.Id」と記述したが、join句では「on x.Id equals y.Id」と記述していることである。より具体的にいえば、「==」という汎用の演算子を使っているか、「equals」というjoin句専用キーワードを使っているかの相違である。つまり、where句で条件を指定する場合は、あらゆる式を自由に記述できたが、join句では「等価」という判定しか行うことができない。
この制約は、逆にいえば、join句を使用するには「等価」と判定できる値を2つのソースが含んでいなければならないことも示す。
from句とjoin句のパフォーマンス
さて、join句は制約が大きいことから、できればfrom句を使いたいと思う読者もいるだろう。果たして、from句とjoin句の差はどれぐらいあるのだろうか。
ここでは、上のリスト17を少し変更し、クエリ式からIdの値が何回参照されているかを調べてみよう。
| |
リスト19 Idの参照回数を調べる |
| |
リスト19の実行結果(from句の場合) |
| |
リスト19の実行結果(join句の場合) |
この結果を見て分かるとおり、from句を2つ使った場合、クエリ対象の情報は、
ソース1の個数(3)×ソース2の個数(3)×2=18回
の参照だが、join句を使用した場合は、
ソース1の個数(3)+ソース2の個数(3)=6回
しか参照されていない。これは、クエリ時のデータ参照の回数がfrom句では掛け算で増えるのに対して、join句では足し算で増えることを意味する。つまり、データの個数が増えれば増えるほど回数の差は劇的に増えていく。
もちろん、データを参照する回数だけでパフォーマンスが決まるわけではない。では、具体的な時間差はどの程度あるのだろうか。以下はそれを調べるために作成したソース・コードである。
| |
リスト20 from句とjoin句の時間差を調べる |
| |
リスト20の実行結果(デバッグ・ビルド時。筆者のPCでの結果) |
このように、処理時間としても、歴然とした差が見て取れる。また、データの個数が増えれば、差はさらに拡大するだろう。両者には歴然とした効率の差があると考えてよいだろう。join句が使用できるときは、join句の利用がお勧めである。
次回予告
join句の主な使い方にはここで紹介した「内部結合」のほかに、「グループ化結合」と「左外部結合」もある。また、クエリ式にはメソッド構文という別の書き方もある。クエリのインスタンス化やlet句なども解説が残っている。
LINQの解説はまだまだ入り口にすぎない。筆者も、LINQの深層に触れてワクワクしている。次回に続く。
INDEX | ||
C# 3.0入門 | ||
第6回 LINQ基礎編 | ||
1.LINQの面白さ/LINQとは何か?/「値の集まり」に対する演算/なぜLINQなのか | ||
2.最も基本的なLINQ/本質は列挙/結果の加工/複数ソース/絞り込み | ||
3.ソート/クエリの接続/グループ化/join句/from句とjoin句のパフォーマンス | ||
「C# 3.0入門」 |
- 第2回 簡潔なコーディングのために (2017/7/26)
ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている - 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう - 第1回 明瞭なコーディングのために (2017/7/19)
C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える - Presentation Translator (2017/7/18)
Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
|
|