私は最近、.NETスタックを使用する役職に対する厳しい技術面接に耐える過程にあります この中には、このような愚かな質問が含まれています 、およびより有効な質問があります。最近、有効な問題に遭遇しましたが、こちらのコミュニティで確認したいと思います。
面接官から、テキストドキュメント内の単語の頻度を数え、結果をランク付けする方法を尋ねられたとき、私は
.GroupBy()
および.Count()
に変換し、次にOrderBy()
でカウントを示します。私はこの答えを2つの理由で間違っていました:
最初の理由は、まあ、合理的なようです。しかし、2番目は、より多くの休止を私に与えます。 LINQのセールスポイントの1つは、ハッシュテーブルのような下位レベルの操作を単純に抽象化することですが、ベールの下では、同じ実装です。
抽象化されたメソッドを呼び出すためのいくつかの追加の処理サイクルに加えて、LINQは、特定のデータ反復タスクを実行するために、低レベルよりも大幅により多くの処理サイクルを必要としますか?タスク(ハッシュテーブルの作成など)はどうでしょうか。
この回答の主な弱点は、Linqの使用が少なく、特定の演算子が選択されていることです。 GroupBy
は各要素を受け取り、それをキーと値に射影し、ルックアップに入ります。つまり、すべてのWordがルックアップに何かを追加します。
素朴な実装.GroupBy(e => e)
は、すべてのWordのコピーをソースマテリアルに格納し、最終的なルックアップを元のソースマテリアルとほぼ同じ大きさにします。 .GroupBy(e => e, e => null)
を使用して値を投影しても、小さな値の大きなルックアップを作成しています。
ここで必要なのは、必要な情報のみを保存する演算子です。これは、各Wordの1つのコピーと、それまでのそのWordの数です。そのためには、Aggregate
を使用できます。
words.Aggregate(new Dictionary<string, int>(), (counts, Word) =>
{
int currentCount;
counts.TryGetValue(Word, currentCount);
counts[Word] = currentCount + 1;
return counts;
}
ここから、これを速くするためにいくつかの方法があります。
面接官は彼が何を話しているのか本当に知りませんでした。さらに悪いことに、彼は「正しい」答えがあると信じています。彼があなたが働きたい人であったなら、私は彼があなたの最初の答えを取り、あなたがそれを選んだ理由を見つけ、それから彼がそれに問題を見つけることができるならそれを改善するようにあなたに挑戦することを期待します。
LINQは魔法のように見えるので、人々を怖がらせます。それは実際には非常に単純です(非常に単純なので、それを思いつくには天才である必要があります)
var result = from item in collection where item=>item.Property > 3 select item;
にコンパイルされます:
IEnumerable<itemType> result = collection.Where(item=>item.property >3);
(もし私が構文を間違えた場合は、叫ばないでください。それは真夜中過ぎで、ベッドにいます:))
ラムダを使用するIEnumerableの拡張メソッドはどこにありますか。ラムダは単に(この場合)デリゲートにコンパイルされます。
bool AMethod(ItemType item)
{
return item.property >3;
}
Whereメソッドは、AMethodがtrueを返す項目のすべてのインスタンスを、返されるコレクションに追加するだけです。
Foreachを実行して、ループ内のすべての一致するアイテムをコレクションに追加するよりも遅い理由はありません。実際には、Where拡張メソッドがおそらくまさにそれを行っています。真の魔法は、代替のwhere基準を注入する必要がある場合に発生します。
上記のコメントで述べたように、リンクされている例のいくつかは非常に間違っています。そして、問題を引き起こすのはこの種の誤報です。
最後に、面接でチャンスが与えられた場合、次のように言うことができます。
LINQは読みやすく、特に興味深い予測とグループ化の導入を開始する場合に便利です。読みやすいコードは、勝つコードを維持するのが簡単です。
それが実際にボトルネックである場合、パフォーマンスを測定し、それを別のものに置き換えることは本当に簡単です。