2011-01-06 4 views
2

私はこのコードからいくつかの複製を削除しようとしており、より多くのパラメータを持つ関数を簡単にサポートしています。リファクタリングと複写をこのMemoizationコードから削除する

このコードをどのように改善し、より複雑な機能を使用できますか?

また、キーの生成について心配しています。オブジェクトによっては文字列に明示的にシリアル化されず、固有の値ではなく型名が返されます。提案?

編集:私はChaosPandionの答えを使用し、ここでは、この

using System; 
using System.Web.Caching; 

public static class Memoize 
{ 
    public static Cache LocalCache = System.Web.HttpRuntime.Cache ?? System.Web.HttpContext.Current.Cache; 

    public static TResult ResultOf<TArg1, TResult>(Func<TArg1, TResult> func, long durationInSeconds, TArg1 arg1) 
    { 
     var key = HashArguments(func.Method.Name, arg1); 
     return ResultOf(key, durationInSeconds,() => func(arg1)); 
    } 

    public static TResult ResultOf<TArg1, TArg2, TResult>(Func<TArg1, TArg2, TResult> func, long durationInSeconds, TArg1 arg1, TArg2 arg2) 
    { 
     var key = HashArguments(func.Method.Name, arg1, arg2); 
     return ResultOf(key, durationInSeconds,() => func(arg1, arg2)); 
    } 

    public static TResult ResultOf<TArg1, TArg2, TArg3, TResult>(Func<TArg1, TArg2, TArg3, TResult> func, long durationInSeconds, TArg1 arg1, TArg2 arg2, TArg3 arg3) 
    { 
     var key = HashArguments(func.Method.Name, arg1, arg2, arg3); 
     return ResultOf(key, durationInSeconds,() => func(arg1, arg2, arg3)); 
    } 

    public static TResult ResultOf<TArg1, TArg2, TArg3, TArg4, TResult>(Func<TArg1, TArg2, TArg3, TArg4, TResult> func, long durationInSeconds, TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4) 
    { 
     var key = HashArguments(func.Method.Name, arg1, arg2, arg3, arg4); 
     return ResultOf(key, durationInSeconds,() => func(arg1, arg2, arg3, arg4)); 
    } 

    private static TResult ResultOf<TResult>(string key, long durationInSeconds, Func<TResult> func) 
    { 
     return LocalCache.Get(key) != null 
        ? (TResult)LocalCache.Get(key) 
        : CacheResult(key, durationInSeconds, func()); 
    } 

    public static void Reset() 
    { 
     var enumerator = LocalCache.GetEnumerator(); 
     while (enumerator.MoveNext()) 
      LocalCache.Remove(enumerator.Key.ToString()); 
    } 

    private static T CacheResult<T>(string key, long durationInSeconds, T value) 
    { 
     LocalCache.Insert(key, value, null, DateTime.Now.AddSeconds(durationInSeconds), new TimeSpan()); 
     return value; 
    } 

    private static string HashArguments(params object[] args) 
    { 
     if (args == null) 
      return "noargs"; 

     var result = 23; 
     for (var i = 0; i < args.Length; i++) 
     { 
      var arg = args[i]; 
      if (arg == null) 
      { 
       result *= (31 * i + 1); 
       continue; 
      } 
      result *= (31 * arg.GetHashCode()); 
     } 
     return result.ToString(); 
    } 
} 
+0

@stackoverflow.com/questions/2852161/c-memoization-of-functions-with-arbitrary-number-of-arguments –

+0

@Alexiei Levenkov、私はそれを読んでいますが、より多くの議論を可能にするために繰り返されなければならなかったコードセクションでした – CaffGeek

+0

いくつかの奇妙なエッジケースをカバーする私の最新バージョンをチェックしてください。 – ChaosPandion

答えて

0

にそれを持っている重複のかなりを削除し、キーのはるかに信頼できる範囲を生成しなければならない私のバージョンです。

EDIT

私の最新バージョンはかなり信頼性の分布を生成します。 Mainメソッドに配置した例を見てください。

class Program 
{ 

    public static class Memoize 
    { 
     public static Cache LocalCache = 
      System.Web.HttpRuntime.Cache ?? System.Web.HttpContext.Current.Cache; 

     public static TResult ResultOf<TArg1, TResult>(
      Func<TArg1, TResult> func, 
      TArg1 arg1, 
      long durationInSeconds) 
     { 
      var key = HashArguments(
       func.Method.DeclaringType.GUID, 
       typeof(TArg1).GUID, 
       (object)arg1); 
      return Complete(key, durationInSeconds,() => func(arg1)); 
     } 

     public static TResult ResultOf<TArg1, TArg2, TResult>(
      Func<TArg1, TArg2, TResult> func, 
      TArg1 arg1, 
      TArg2 arg2, 
      long durationInSeconds) 
     { 
      var key = HashArguments(
       func.Method.DeclaringType.GUID, 
       typeof(TArg1).GUID, 
       (object)arg1, 
       typeof(TArg2).GUID, 
       (object)arg2); 
      return Complete(key, durationInSeconds,() => func(arg1, arg2)); 
     } 

     public static TResult ResultOf<TArg1, TArg2, TArg3, TResult>(
      Func<TArg1, TArg2, TArg3, TResult> func, 
      TArg1 arg1, 
      TArg2 arg2, 
      TArg3 arg3, 
      long durationInSeconds) 
     { 
      var key = HashArguments(
       func.Method.DeclaringType.GUID, 
       typeof(TArg1).GUID, 
       (object)arg1, 
       typeof(TArg2).GUID, 
       (object)arg2, 
       typeof(TArg3).GUID, 
       (object)arg3); 
      return Complete(key, durationInSeconds,() => func(arg1, arg2, arg3)); 
     } 

     public static void Reset() 
     { 
      var enumerator = LocalCache.GetEnumerator(); 
      while (enumerator.MoveNext()) 
       LocalCache.Remove(enumerator.Key.ToString()); 
     } 

     private static T CacheResult<T>(string key, long durationInSeconds, T value) 
     { 
      LocalCache.Insert(
       key, 
       value, 
       null, 
       DateTime.Now.AddSeconds(durationInSeconds), 
       new TimeSpan()); 
      return value; 
     } 

     static T Complete<T>(string key, long durationInSeconds, Func<T> valueFunc) 
     { 
      return LocalCache.Get(key) != null 
       ? (T)LocalCache.Get(key) 
       : CacheResult(key, durationInSeconds, valueFunc()); 
     } 

     static string HashArguments(params object[] args) 
     { 
      if (args == null) 
       return "null args"; 

      int result = 23; 
      for (int i = 0; i < args.Length; i++) 
      { 
       var arg = args[i]; 
       if (arg == null) 
       { 
        result = 31 * result + (i + 1); 
        continue; 
       } 
       result = 31 * result + arg.GetHashCode(); 
      } 
      return result.ToString(); 
     } 
    } 

    static int test(int a, int b) 
    { 
     return a + b; 
    } 

    private static class Inner 
    { 
     public static int test(int a, int b) 
     { 
      return a + b; 
     } 
    } 

    static int test(int a, object b) 
    { 
     return a + (int)b; 
    } 

    static void Main(string[] args) 
    { 
     Memoize.ResultOf<int, int, int>(test, 1, 2, 100000); 
     Memoize.ResultOf<int, int, int>(test, 2, 1, 100000); 
     Memoize.ResultOf<int, int, int>(Inner.test, 1, 2, 100000); 
     Memoize.ResultOf<int, int, int>(Inner.test, 2, 1, 100000); 
     Memoize.ResultOf<int, object, int>(test, 1, 2, 100000); 
     Memoize.ResultOf<int, object, int>(test, 2, 1, 100000); 
    } 
} 
+0

@ChaosPandion、 'HashArguments'で何が起こっているのか説明できますか?私はそれを得るのですが、どうして私は 'GetHashCode()'をいつも使うことができないのですか? – CaffGeek

+0

@Chad - ある関数に2つの整数引数があるとします。 'GetHashCode'を呼び出すと、同じ整数値が返されます。単純に 'GetHashCode' 2を使用するならば、完全に異なる組み合わせは同じ結果を生み出します。 – ChaosPandion

+0

補足として、 'Complete'で' LocalCache.Get'を2回呼ぶのは避けたいと思います。 –

0

ChaosPandionのコード内の基本的な考え方は正しいのですが、これは確実に動作するために固定しなければならないいくつかの致命的な欠陥があります。

1)ハッシュのハッシュを使用するのではなく、真にユニークなキーを作成する必要があります。 1つの方法は、パラメータ配列内の各要素の文字列表現をデリミタで連結することです。このようにして、結果がメモされると、偽陽性を伴わずに一貫して検索されます。

このメソッドのGUIDを使ったカオスのトリックはここでは便利ですが、GetHashCodeが本当にユニークであるという問題や、ハッシュのかなり単純なハッシュをユニークなままにするという問題は回避されます。代わりに、完全な文字列がハッシュされますが、不一致を除外するために完全な文字単位の比較が行われます。

確かに、大きな文字列を連結することは、特にエレガントではなく、パフォーマンスに影響を与える可能性があります。さらに、これは依然としてToStringがインスタンス固有の情報を与えないクラスの問題を残します。しかし、これには反射に基づく解決策があります。

2)LocalCache.Getをちょうど1回Completeと呼ぶことが非常に簡単です。これは潜在的に高価な操作なので、コストを倍増させる理由はありません。

それ以外は、ChaosPandionの提案に従ってください。

関連する問題