2012-03-22 58 views
1

我有一个简单的memoizer装饰:memoize的装饰未能memoize的(不使用修饰语法时)

def funcmemo(f): 
    memo = {} 
    @wraps(f) 
    def wrapper(*args): 
    if args in memo: 
     return memo[args] 
    else: 
     temp = f(*args) 
     print "memoizing: ", args, temp 
     memo[args] = temp 
     return temp 
    return wrapper 

现在,当我通过 “@” 符号使用,

@funcmemo 
def fib(n): 
    print "fib called with:", n 
    if n < 2: return n 
    return fib(n-2) + fib(n-1) 

res = fib(3) 
print "result:", res 

它工作正常,如在打印输出中看到:

fib called with: 3 
fib called with: 1 
memoizing: (1,) 1 
fib called with: 2 
fib called with: 0 
memoizing: (0,) 0 
memoizing: (2,) 1 
memoizing: (3,) 2 
result: 2 

然而,当我这样做:

def fib(n): 
    print "fib called with:", n 
    if n < 2: return n 
    return fib(n-2) + fib(n-1) 

memfib = funcmemo(fib) 
res = memfib(3) 
print "result:", res 

显然未修饰的FIB被调用,只有最后的返回值“达到”高速缓存(显然产生了巨大的经济放缓):

fib called with: 3 
fib called with: 1 
fib called with: 2 
fib called with: 0 
fib called with: 1 
memoizing: (3,) 2 
result: 2 

奇怪的是,这一个正常工作:

def fib(n): 
    print "fib called with:", n 
    if n < 2: return n 
    return fib(n-2) + fib(n-1) 

fib = funcmemo(fib) 
res = fib(3) 
print "result:", res 

而且,同样的事情发生与基于类的版本:

class Classmemo(object): 
    def __init__ (self, f): 
     self.f = f 
     self.mem = {} 
    def __call__ (self, *args): 
     if args in self.mem: 
      return self.mem[args] 
     else: 
      tmp = self.f(*args) 
      print "memoizing: ", args, temp 
      self.mem[args] = tmp 
      return tmp 
使用“匿名”装饰功能时,就像

res = Classmemo(fib)(3) 

我会很高兴得到启发关于这背后的原因也发生

的问题。

+0

是的,你是对的。但你的问题在哪里? – Alfe 2012-03-22 09:54:43

回答

5

对此没有什么好奇的。当你做

memofib = funcmemo(fib) 

你不改变功能fib点以任何方式,但创建一个新功能和指向它的名字memofib

所以当memofib被调用,它调用的函数的名称为fib指出 - 这递归调用本身,而不是memofib - 所以不会发生记忆化。

在你的第二个例子,你做

fib = funcmemo(fib) 

所以递归调用自己和记忆化发生在各个层面。

如果你不希望覆盖名fib,作为装饰版本或你的第二个例子呢,你可以改变fib采取函数名称:

def fib(n, fibfunc): 
    print "fib called with:", n 
    if n < 2: return n 
    return fibfunc(n-2, fibfunc) + fibfunc(n-1, fibfunc) 

memofib = funcmemo(fib) 
res = fib(3, memofib) 

你也可以使用一个fixed point combinator来避免将fibfunc每次:

def Y(f): 
    def Yf(*args): 
     return f(Yf)(*args) 
    return f(Yf) 

@Y 
def fib(f): 
    def inner_fib(n): 
     print "fib called with:", n 
     if n < 2: return n 
     return f(n-2) + f(n-1) 
    return inner_fib 
+0

我明白了..谢谢。 – 2012-03-22 09:59:17

+0

另外,感谢设置我通过提出固定点组合器完成大脑崩溃的课程。 – 2012-03-22 12:32:00

+0

@AndrásKovács我无法抗拒:)。只要我输入了传递函数的版本,我意识到这可能是我唯一能够在问题中没有具体提及的时候使用的。 – agf 2012-03-22 12:36:06

2

如果你的问题只是一个简单的为什么,我想答案是仅仅因为recu与fib()进行的rsion-call调用名称为fib()的函数。要装饰你必须替换指针的值fib;这不是由memfib = funcmemo(fib)也不是由类版本完成的。