2

日付と値を含むオブジェクトのリストがあります。日付ごとに 1 つのオブジェクトと、過去数か月のすべての日付のオブジェクトがあります。値が最新の値に変更された日付を探しています。

これが私が意味することの例です:

<datevalue>
    <date>8-9</date>
    <value>5</value>
</datevalue>
<datevalue>
    <date>8-10</date>
    <value>6</value>
</datevalue>
<datevalue>
    <date>8-11</date>
    <value>5</value>
</datevalue>
<datevalue>
    <date>8-12</date>
    <value>5</value>
</datevalue>
<datevalue>
    <date>8-13</date>
    <value>5</value>
</datevalue>

上記の例では、最新の日付である 8 ~ 13 日の値であるため、現在の値は 5 です。値が最新の値に変更された日なので、8-11 の datevalue オブジェクトを返したいです。現在の値で最も早い日であっても、その日付の後に値が変更されたため、8-9 の値は必要ありません。

これを解決するための私の最初の試みは次のとおりです。

DateValue FindMostRecentValueChange(List<DateValue> dateValues)
{
    var currentValue = dateValues
                        .OrderByDesc(d => d.date)
                        .Select(d => d.value)
                        .First();
    var mostRecentChange = dateValues
                            .OrderByDesc(d => d.date)
                            .TakeWhile(d => d.value = currentValue)
                            .Last();
    return mostRecentChange;
}

これは機能します。ただし、両方の操作で OrderByDesc を繰り返していることが指摘されました。OrderByDesc はコストのかかる操作になる可能性があることを考慮して、2 回実行する必要がないようにしたいと考えました。したがって、私は変更を加えました:

DateValue FindMostRecentValueChange(List<DateValue> dateValues)
{
    var orderedDateValues = dateValues.OrderByDesc(d => d.date);
    var currentValue = orderedDateValues;
                        .Select(d => d.value)
                        .First();
    var mostRecentChange = orderedDateValues
                            .TakeWhile(d => d.value = currentValue)
                            .Last();
    return mostRecentChange;
}

今度は OrderByDesc を 1 回だけ呼び出します。改善ですね。まあ、そうではないかもしれません。OrderByDesc は遅延実行です。

私が理解していることから、それは、実際の注文は、値を要求するまで行われないことを意味します。したがって、currentValue を探しているときに First() を呼び出すと、OrderByDesc が実行され、mostRecentChange を探しているときに Last() を呼び出すと、もう一度実行されます。ということは、まだ OrderByDesc を 2 回実行しているということですか?

遅延実行がどのように機能するかを正しく解釈していますか? コンパイラがこのシナリオを認識し、実行が 1 回だけ呼び出されるように舞台裏で最適化してくれることを願っていますが、この理論を裏付ける情報が見つかりません。このソリューションを最適化するための最善の方法について頭を悩ませるのを手伝ってもらえますか?

4

2 に答える 2

3

ということは、まだ OrderByDesc を 2 回実行しているということですか?

はい、そうです。

コンパイラがこのシナリオを認識し、実行が 1 回だけ呼び出されるように舞台裏で最適化してくれることを願っていますが、この理論を裏付ける情報が見つかりません。

意図した機能がいくつかの重要な点で変更されるため、できません。

  1. 基になるデータが変更された場合、シーケンスを再度反復するときにそれらの変更を反映する必要があります。最初のクエリと 2 番目のクエリの間に新しい項目を追加した場合dateValues、2 番目のクエリにあるはずです。アイテムを削除した場合、そこには存在しないはずです。

  2. 求めているものを取得するには、最初のコンシューマーが「完了」した後でも、すべてのアイテムを何らかのコレクションに保存する必要があります。それは望ましくありません。ここでの考え方は、データをストリーミングできるということです。アイテムの処理が終了したら、そのアイテムは「完了」し、メモリに保持する必要はありません。後続の実行のためにクエリ内のすべてのアイテムを保持するのに十分なメモリがない場合はどうなるでしょうか?

このソリューションを最適化するための最善の方法について頭を悩ませるのを手伝ってもらえますか?

それは非常に些細なことです。クエリの結果をデータ構造に入力するだけです。これを行う最も簡単な方法は、それらをすべてリストに入れることです。クエリの最後に呼び出しを追加するToListと、一度評価され、結果のリストを何度も繰り返しても悪影響はありません。このようなセマンティクスが必要な場合、このソリューションは非常に簡単に取得できますが、遅延実行のセマンティクスは、より強力であるにもかかわらず取得するのがはるかに難しいため、具体化されたコレクションに基づく LINQ を使用しないことを選択しました。

于 2013-08-13T19:19:41.710 に答える