2017-08-14 6 views
0

私はそうのようなmemoizer機能を持っている:使用メモ化方法

static Func<A, R> Memoize<A, R>(this Func<A, R> f) 
{ 
    var cache = new ConcurrentDictionary<A, R>(); 
    return argument => cache.GetOrAdd(argument, f); 
} 

そして私も今、私の主な機能には、私がしようと、いくつかの再帰的な方法に

long TheRecursiveMeth (string inString) { 
// recursive function that calls itself  
} 

をしている:

TheRecursiveMeth = TheRecursiveMeth.Memoize(); 

コンパイラは

を不平を言います

'。'オペレータは、割り当ての左辺は、変数、プロパティまたは インデクサ

なければならないメソッドグループ」 '型のオペランドに適用することができない

TheRecursiveMethへの呼び出しを実際にTheRecursiveMeth.Memoize()と呼びます(再帰呼び出しを含む)?

編集:私はTheRecursiveMethの定義を編集しないようにしています。明らかに、私はちょうど辞書をチェックすることができます。

編集2:興味があるので、特定の文字列の特定の回文の数を数える関数があります。それは、ここで説明して少し複雑ですが、基本的には何か:

long palCount(string inString) { 
    if (inString.Length==1) return 1; 
    else { 
     count = 0; 
     foreach(substring of inString) { 
      // more complex logic here 
      count += palCount(subString); 
     } 
     return count; 
    } 
} 

だから明確なもののこのタイプは、メモ化の恩恵を受けるだろう。私は最初にアルゴリズムを追加することを避けました。なぜなら、それは無関係であり、人々が私に提案を与える可能性が高いからです。それはポイントの横にあります。

+1

おそらく 'var memoized = Memoize (theRecursiveMeth)'ですか? –

+0

ええ、再帰的な呼び出しは、それを使用するつもりはありませんか? – dashnick

+1

あなたが何を求めているのか分かりません。エラーメッセージは私にとっては非常にはっきりしているようで、許可されていないという明白な理由があります。とりわけ混乱させることは、メソッド名自体を再割り当てしようとするあなたの明白な試みと、すべての呼び出しで新しいキャッシュを作成するようなMemoize()の実装を覚えているので、暗記のメリットがなくなります。上記の最初のコメントの提案のようなものはうまくいくでしょうが(そしてあなたができることはすべてあります)、あなたの実際のシナリオにどのように適合するかを理解するのは混乱します。 –

答えて

1

完全に一般化可能なソリューションでは、これをすべて機能させるために関数を書く方法を変更する必要があります。

メモ帳機能をインスタンス化しようとしていた問題を無視して、主な問題は、初期バインディングのためメモ機能を呼び出すことができず、コンパイラは常に元の関数にバインドされます。あなたは、関数の実装にメモをつけたバージョンへの参照を与え、それを使用させる必要があります。 memoized関数と同じシグネチャを持つファンクションとargsの両方を持つファクトリを使用して、これを達成できます。

public static Func<TArgs, TResult> Memoized<TArgs, TResult>(Func<Func<TArgs, TResult>, TArgs, TResult> factory, IEqualityComparer<TArgs> comparer = null) 
{ 
    var cache = new ConcurrentDictionary<TArgs, TResult>(comparer ?? EqualityComparer<TArgs>.Default); 
    TResult FunctionImpl(TArgs args) => cache.GetOrAdd(args, _ => factory(FunctionImpl, args)); 
    return FunctionImpl; 
} 

これは、その後のような機能を変更します。

public long Fib(long n) 
{ 
    if (n < 2L) 
     return 1L; 
    return Fib(n - 1) + Fib(n - 2); 
} 

これまで:キックのために

private Func<long, long> _FibImpl = Memoized<long, long>((fib, n) => 
{ 
    if (n < 2L) 
     return 1L; 
    return fib(n - 1) + fib(n - 2); // using `fib`, the parameter passed in 
}); 
public long Fib(long n) => _FibImpl(n); 

は、ここでCastle DynamicProxyで使用することができmemoizerインターセプターの実装です。

class MemoizedAttribute : Attribute { } 

class Memoizer<TArg, TResult> : IInterceptor 
{ 
    private readonly ConcurrentDictionary<TArg, TResult> cache; 
    public Memoizer(IEqualityComparer<TArg> comparer = null) 
    { 
     cache = new ConcurrentDictionary<TArg, TResult>(comparer ?? EqualityComparer<TArg>.Default); 
    } 
    public void Intercept(IInvocation invocation) 
    { 
     if (!IsApplicable(invocation)) 
     { 
      invocation.Proceed(); 
     } 
     else 
     { 
      invocation.ReturnValue = cache.GetOrAdd((TArg)invocation.GetArgumentValue(0), 
       _ => 
       { 
        invocation.Proceed(); 
        return (TResult)invocation.ReturnValue; 
       } 
      ); 
     } 
    } 
    private bool IsApplicable(IInvocation invocation) 
    { 
     var method = invocation.Method; 
     var isMemoized = method.GetCustomAttribute<MemoizedAttribute>() != null; 
     var parameters = method.GetParameters(); 
     var hasCompatibleArgType = parameters.Length == 1 && typeof(TArg).IsAssignableFrom(parameters[0].ParameterType); 
     var hasCompatibleReturnType = method.ReturnType.IsAssignableFrom(typeof(TResult)); 
     return isMemoized && hasCompatibleArgType && hasCompatibleReturnType; 
    } 
} 

次に、メソッドが仮想宣言され、適切な属性が設定されていることを確認してください。

public class FibCalculator 
{ 
    [Memoized] 
    public virtual long Fib(long n) 
    { 
     if (n < 2L) 
      return 1L; 
     return Fib(n - 1) + Fib(n - 2); 
    } 
} 

var calculator = new Castle.DynamicProxy.ProxyGenerator().CreateClassProxy<FibCalculator>(new Memoizer<long, long>()); 
calculator.Fib(5); // 5 invocations 
calculator.Fib(7); // 2 invocations 
+0

ありがとう! Pythonではこのようなことをデコレータで行っています。ここで役立つ属性で何かできますか? – dashnick

+0

バニラフレームワークを使用して、私が知る限りではありません。生成されたアセンブリを書き直すか、関数を書き換えるためにコンパイラフックを追加する必要があります。ただし、同様の結果を得るために動的なプロキシを作成することもできます。それはもっと厄介な解決策かもしれません。 [Castle Project](http://www.castleproject.org/projects/dynamicproxy/)(私はこれを自分で使っていない)のようなものです。あなたはこのアプローチの代わりにそれを完全に使用することができます。 –

+0

ありがとうございました。 'Memoized'関数がすべての呼び出しで新しい辞書を作成する理由を何らかの理由で説明できますか?キャッシュは状態の一部であり、関数内のローカル変数ではありませんか?私は元のhttps://stackoverflow.com/a/20544642/3661120をここから入手しましたが、どちらかといえばそれを下回りませんでした。 – dashnick

関連する問題