C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[4]: LINQの仕様


(過去ログ 140 を表示中)

[トピック内 6 記事 (1 - 6 表示)]  << 0 >>

■82184 / inTopicNo.1)  LINQの仕様
  
□投稿者/ ぶなっぷ (97回)-(2016/12/15(Thu) 09:00:36)

分類:[.NET 全般] 

ふとした疑問がわきました。
LINQは便利ですね、C#のソースコードを見ると、誰もが多用しています。

そんな中で以下のようなコードを見つけました。
  List<Data> DataList = GetDataList();
  Data TgtData = DataList.Where(x => x.Name == TgtName).FirstOrDefault();

え、わざわざフィルタしてから、最初の要素だけ取得?
最初から1要素だけしか取得しないこっちの方が速くない?
  Data TgtData = DataList.FirstOrDefault(x => x.Name == TgtName);

と思ったのも束の間。
よく考えたら、LINQって遅延評価だよね。
実は、どっちも最初の要素しか取得しないのか?

なんか、よく分からなくなってきたので、識者の意見を拝聴したく。

引用返信 編集キー/
■82185 / inTopicNo.2)  Re[1]: LINQの仕様
□投稿者/ 魔界の仮面弁士 (1023回)-(2016/12/15(Thu) 09:22:43)
No82184 (ぶなっぷ さん) に返信
> LINQは便利ですね、C#のソースコードを見ると、誰もが多用しています。
誰もが多用という表現には首を傾げるところもありますが、
便利という点については同意です。


> なんか、よく分からなくなってきたので
イテレータを使うと確認しやすいかと思います。

GetDataList メソッドの実装を、List<Data> を返すかわりに
IEnumerable<Data> を返すようにして、yield return のところに
ブレークポイントを貼ってみましょう。


> Data TgtData = DataList.Where(x => x.Name == TgtName).FirstOrDefault();
> 最初から1要素だけしか取得しないこっちの方が速くない?
> Data TgtData = DataList.FirstOrDefault(x => x.Name == TgtName);
正しい認識だと思います。
内部処理的には、後者の方が手数が少なくて済みます。


> 実は、どっちも最初の要素しか取得しないのか?
どちらも最初に条件を満たしたところで走査終了となります。
3 番目の要素で合致したら、4 番目以降の要素は走査されません。
引用返信 編集キー/
■82189 / inTopicNo.3)  Re[2]: LINQの仕様
□投稿者/ furu (85回)-(2016/12/15(Thu) 10:51:36)
No82185 (魔界の仮面弁士 さん) に返信
> ■No82184 (ぶなっぷ さん) に返信
>> Data TgtData = DataList.Where(x => x.Name == TgtName).FirstOrDefault();
>>最初から1要素だけしか取得しないこっちの方が速くない?
>> Data TgtData = DataList.FirstOrDefault(x => x.Name == TgtName);
> 正しい認識だと思います。
> 内部処理的には、後者の方が手数が少なくて済みます。

横槍です。

最適化で同じ速度(手数)になるような気がするけど
Where,FirstOrDefaultもメソッドだし、
Nameはプロパティ(そうでもないか)で
なにがあるかわからないから
コンパイラはそのままの流れでやるしかない?
引用返信 編集キー/
■82191 / inTopicNo.4)  Re[2]: LINQの仕様
□投稿者/ 魔界の仮面弁士 (1025回)-(2016/12/15(Thu) 11:55:19)
2016/12/15(Thu) 14:48:12 編集(投稿者)

No82185 (魔界の仮面弁士) に追記
> 内部処理的には、後者の方が手数が少なくて済みます。

Reference Source によれば、各メソッドは、
LINQ ソースに対して、後述の順番で処理されるようです。

.Where を使った場合、処理を private な抽象イテレータクラスに
変換する作業が追加されていますので、手数はそれなりに増えますね。

ただ、最初にマッチしたところで走査が打ち切られる点は変わりませんし、
このことがボトルネックになるほどでは無いでしょう。


=======================================================
【<TSource>.FirstOrDefault() の場合】
-------------------------------------------------------
(1) ソースが IList<TSource> かつ source.Count > 0 の場合は source[0] を return。

(2) 上記以外なら、foreach で先頭要素を return して走査終了。

(3) 上記以外(つまり空要素だった場合)は、return default(TSource);


=======================================================
【<TSource>.FirstOrDefault(lambda) の場合】
-------------------------------------------------------
(1) ソースを foreach しながら、各要素を lambda 条件で判定していき、
 最初にマッチしたものを return して走査終了。

(2) マッチしなければ return default(TSource);


=======================================================
【<TSource>.Where(lamda) の場合】
-------------------------------------------------------
(1) ソースが抽象イテレータクラス Iterator<TSource> であれば、それの
  abstract IEnumerable<TSource> Where(Func<TSource, bool> predicate)
  メソッドの戻り値を return する。

(2) 上記ではなく一次元配列であれば、
  private class WhereArrayIterator<TSource> : Iterator<TSource>
  のコンストラクタに渡し、このインスタンスを return する。

(3) 上記ではなく List<T> クラスであれば、それを
  private class WhereListIterator<TSource> : Iterator<TSource>
  のコンストラクタに渡し、このインスタンスを return する。

(4) どれでも無い場合は、
  private class WhereEnumerableIterator<TSource> : Iterator<TSource>
  のコンストラクタに渡し、このインスタンスを return する。
引用返信 編集キー/
■82192 / inTopicNo.5)  Re[3]: LINQの仕様
□投稿者/ 魔界の仮面弁士 (1026回)-(2016/12/15(Thu) 12:31:27)
No82191 (魔界の仮面弁士) に追記の追記
> Reference Source によれば、各メソッドは、
> LINQ ソースに対して、後述の順番で処理されるようです。

今回は、元質問が IList<T> に対するものだったので、
System.Linq.Enumerable 系の拡張メソッドについて調査しました。

それ以外の LINQ (特に式木タイプ)の場合、また違う結果になるものも
あるのかも知れませんが、そこまでは調査していません。




No82184 (ぶなっぷ さん) に返信
> よく考えたら、LINQって遅延評価だよね。

ほとんどの LINQ は遅延評価として実装されていますが、
遅延評価であることが強制されているわけではありません。


たとえば、
 int[] intArray = Foo();
 var q1 = from a in intArray where a < 0 select a;
 var q2 = intArray.Where(b => b < 0);
の場合、通常であればどちらも遅延評価となりますが、
極端な話、
 public static IEnumerable<int> Where(this int[] x, Func<int, bool> dummy)
のような拡張メソッドを用意しておけば、
 ・ラムダ式が即時評価されるパターン
 ・ラムダ式が全く呼ばれず無視されるパターン
なんてこともありうるわけで。
引用返信 編集キー/
■82193 / inTopicNo.6)  Re[4]: LINQの仕様
□投稿者/ ぶなっぷ (99回)-(2016/12/15(Thu) 12:58:05)
みなさん、どうもです。

>> LINQは便利ですね、C#のソースコードを見ると、誰もが多用しています。
> 誰もが多用という表現には首を傾げるところもありますが、
まぁ、これはあくまで、私が見てきた限りにおいてという話ではあります(^^;)

いろいろあるようだけど、
「最初に条件を満たしたところで走査終了」
という点は間違いなさそうですね。

であれば、大きな速度差にはならないでしょうから、現段階での既存ソース
コードの修正はやめておきます。
また、暇ができたときにでもじっくりと腰を据えて修正にかかるとします。

ありがとうございました。

解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -