2012-04-27 11 views
3

私の開発者の友人は、代理人を使ってループがはるかに高速であることを教えています。私はベンチマークをしたいと思いますが、どのように動作しているのかを点で結びつけています。このシナリオではどのように代理人を使用しますか?

以下の残高計算機を検討してください。これは基本的にアカウントのリストを受け取り、それが存在する場合、総クレジット値に存在し、各アカウントの合計借方値を減算した場合の初期値(開始バランス)を追加します。

private static IDictionary<string, decimal> CalculateBalances(
     IDictionary<string, decimal> initialValue, 
     IDictionary<string, decimal> credits, 
     IDictionary<string, decimal> debits) 
    { 
     var r = new Dictionary<string, decimal>(); 

     foreach (var key in initialValue.Select(k => k.Key) 
      .Concat(credits.Select(k => k.Key)) 
      .Concat(debits.Select(k => k.Key)) 
      .Distinct()) 
     { 
      r.Add(key, 
       (initialValue.ContainsKey(key) ? initialValue[key] : 0M) 
       + (credits.ContainsKey(key) ? credits[key] : 0M) 
       - (debits.ContainsKey(key) ? debits[key] : 0M) 
       ); 
     } 

     return r; 
    } 

これはパフォーマンスかなりです小規模から中規模のアカウントリストでは、デリゲートを使用する方が高速でしょうか?そして、率直に言って、デリゲートロジックは私の思考プロセスに直角に動作しているように思えます。なぜなら、これを書いても私の頭を傷つけているからです。

誰もが代理人を使用してこれを書き換える方法を提供できますか?

+0

参加者は、正確に何よりも速いと思われますか?ラムダのデリゲートを既に使用しています(明示的に作成していなくても、そこにあります)。あなたのループのどの部分を代理人に置き換えるのですか? – Sven

+0

彼女はそれについて話していましたが、基本的にはループ内のすべてのものを代理人呼び出しで置き換えました。彼女が何を話しているのか分かっていたように、私は懐疑的な眉を上げた。 –

+0

"デリゲートロジックは、私の思考プロセスに対して直角に動作しているようです"とは、機能プログラミング言語(F#など)でおもちゃプロジェクトを行い、 "思考のベクトル"が確実に正しい方向に向くようにすることです。 – dasblinkenlight

答えて

5

私はあなたの友人がList<T>クラスのForEachメソッドのようなものを指していると仮定しています。あなたの質問への短い答えはです。です。

同等の構文は次のようになります。

initialValue.Select(k => k.Key) 
      .Concat(credits.Select(k => k.Key)) 
      .Concat(debits.Select(k => k.Key)) 
      .Distinct() 
      .ToList() 
      .ForEach(var => r.Add(key, 
       (initialValue.ContainsKey(key) ? initialValue[key] : 0M) 
       + (credits.ContainsKey(key) ? credits[key] : 0M) 
       - (debits.ContainsKey(key) ? debits[key] : 0M) 
       )); 

これは、あなたが上記のそれを持っている方法よりも良くない方法でです。読むのがより遅く、また困難です。デリゲートの呼び出しは、通常のメソッド呼び出しよりも遅くなります。上記の構文は、より速く、読みやすくなっています。

+0

Hmm。次に 'ForEach'メソッドはどのような状況で' List'オブジェクトで魅力的でしょうか? –

+0

@JeremyHolovacs:私はそれがより良い*なる状況を考えることはできません。 –

+0

リストを作成したら、ここにベンチマークを投稿することを忘れないでください。それは面白いはずです。 –

2

誰もが代理人を使用してこれを書き換える方法を提供できますか?

しかし、は、すでに代理人を使用してです。それはラムダが変換されているものです。 パフォーマンス上の理由からデリゲートをループボディに使用するかどうかについての質問は、シーケンスの各アイテムを生成するために非常に多くのデリゲート呼び出しが使用されているときに少し奇妙です。

とにかく、リストの各項目とそれに関連する可読性とパフォーマンスの関係に副作用を実行する方法については、List.ForEachですでに説明していますので、ここでは取り上げません。

しかし、ここでは、LINQとデリゲートの呼び出しの限界オーバーヘッドが決定的な要因でなかった場合、私はあなたの方法を記述します方法は次のとおりです。

return initialValue 
     .Concat(credits) 
     .Concat(debits.Select(kvp => new KeyValuePair<string, decimal>(kvp.Key, -kvp.Value))) 
     .GroupBy(kvp => kvp.Key, kvp => kvp.Value) 
     .ToDictionary(group => group.Key, group => group.Sum()); 

は、今でははるかに読みやすいです。

0

Dictionary.Addを使用して新しい辞書を作成する場合は、foreach構造が正しいです。既存の辞書から選択して新しい辞書を作成することはできますが、それは遅くなります。

ただし、読みやすさに関しては、それほど簡単ではありませんか?

private static decimal GetOrZero(this IDictionary<string,decimal> dict, string key) 
{ 
    decimal value = 0; 
    dict.TryGetValue(key, out value); 
    return value; 
} 

private static IDictionary<string, decimal> CalculateBalances(
    IDictionary<string, decimal> initialValue, 
    IDictionary<string, decimal> credits, 
    IDictionary<string, decimal> debits) 
{ 
    var r = new Dictionary<string, decimal>(); 
    var accounts = initialValue.Keys.Union(debits.Keys).Union(credits.Keys); 

    foreach (var accounts in accounts) 
    { 
     r.Add(initialValue.GetOrZero(key) + credits.GetOrZero(key) - debits.GetOrZero(key)); 
    } 

    return r; 
} 
関連する問題