2017-08-14 129 views
0

使用Memoized方法我有一個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; 
    } 
} 

很明顯,這種類型的東西會受益於memoization。我首先避免添加算法,因爲它是無關緊要的,並且更有可能讓人們給我提出建議,這是不言而喻的。

+1

也許'var memoized = Memoize (theRecursiveMeth)'? –

+0

是的,但遞歸調用不會使用它,對吧? – dashnick

+1

我不明白你在問什麼。錯誤信息對我來說似乎很清楚,並且有明顯的不被允許的原因。特別令人困惑的是,你明顯嘗試重新分配方法名稱本身_以及似乎在每次調用時創建新緩存的Memoize()實現,因此否定了記憶的益處。類似於上面第一條評論中的建議可以很好地工作(並且您將能夠完成所有工作),但問題太混亂,無法理解這將如何適合您的實際情況。 –

回答

1

對於完全一般化的解決方案,您必須更改編寫函數的方式才能使這一切工作。

忽略你試圖實例化你的memoized函數的問題,主要問題是由於早期綁定,你不能調用你的memoized函數,編譯器將始終綁定到原始函數。你需要給你的函數實現一個對memoized版本的引用,並讓它使用它。你可以使用一個工廠來實現這個功能,這個工廠同時帶有一個與memoized函數相同的簽名的函數和參數。

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