2

計算に時間がかかる可能性のある値を使用して、オブジェクトのリストを並べ替えたいと思います。今のところ私はこのようなコードを持っています:

public IEnumerable<Foo> SortFoo(IEnumerable<Foo> original)
{
    return foos.OrderByDescending(foo => CalculateBar(foo));
}

private int CalculateBar(Foo foo)
{
    //some slow process here
}

上記のコードの問題は、アイテムごとに値の計算を数回呼び出すことですが、これは良くありません。可能な最適化は、キャッシュされた値(おそらく辞書)を使用することですが、SortFooは各ソート後にキャッシュをクリアする必要があることを意味します(メモリリークを回避するため、SortFoo呼び出しごとに値を再計算する必要があります)。

この問題に対するよりクリーンでエレガントな解決策はありますか?

4

2 に答える 2

6

遅いs.OrderBy()用にすでに最適化されているようです。keySelector

以下に基づいて、あなたがそれを提供するデリゲート.OrderBy()の結果をキャッシュしているようです。keySelector

var random = new Random(0);
var ordered = Enumerable
    .Range(0, 10)
    .OrderBy(x => {
        var result = random.Next(20);
        Console.WriteLine("keySelector({0}) => {1}", x, result);
        return result;
    });
Console.WriteLine(String.Join(", ", ordered));

出力は次のとおりです。

keySelector(0) => 14
keySelector(1) => 16
keySelector(2) => 15
keySelector(3) => 11
keySelector(4) => 4
keySelector(5) => 11
keySelector(6) => 18
keySelector(7) => 8
keySelector(8) => 19
keySelector(9) => 5
4, 9, 7, 3, 5, 0, 2, 1, 6, 8

比較ごとにデリゲートを1回実行している場合、keySelectorアイテムごとにデリゲートの呼び出しが複数回表示されます。

于 2014-06-18T16:53:34.543 に答える
4

各アイテムはソートで他のアイテムと複数回比較されるため、少なくともアイテムごとに1つの計算を安価にキャッシュできます。同じ値に対して計算を頻繁に実行する場合は、関数をメモ化するのが最善の策です。

public IEnumerable<Foo> SortFoo(IEnumerable<Foo> original)
{
    return foos
        .Select(f => new { Foo = f, SortBy = CalculateBar(f) })
        .OrderByDescending(f=> f.SortBy)
        .Select(f => f.Foo);
}

これにより、計算がアイテムごとに1回に減ります

于 2012-07-11T10:56:17.220 に答える