私は最近、デコレータを使用して再帰関数をメモ化する強力な方法について学びました。
「よしよし、遊んでみよう!」
class memoize:
"""Speeds up a recursive function"""
def __init__(self, function):
self.function = function
self.memoized = {}
def __call__(self, *args):
try:
return self.memoized[args]
except KeyError:
self.memoized[args] = self.function(*args)
return self.memoized[args]
#fibmemo
@memoize
def fibm(n, current=0, next=1):
if n == 0:
return current
else:
return fibm(n - 1, next, current+next)
これtimeit
は、実際にアルゴリズムを高速化すること
を示しています。
fibmemo 0.000868436280412
fibnorm 0.0244713692225
「うわー、それは本当に役に立つかもしれません! 私はそれをどこまでプッシュできるのだろうか?」
140 を超える入力を使い始めると、すぐにRuntimeError: maximum recursion depth exceeded
. 「ああ、クソ..」
少し検索した後、問題を解決するように見えるハックを見つけました。
「これもいいですね!遊んでみましょう」
class TailRecurseException:
def __init__(self, args, kwargs):
self.args = args
self.kwargs = kwargs
def tail_call_optimized(g):
"""
This function decorates a function with tail call optimization. It does this by throwing an exception
if it is it's own grandparent, and catching such exceptions to fake the tail call optimization.
This function fails if the decorated function recurses in a non-tail context.
"""
def func(*args, **kwargs):
f = sys._getframe()
if f.f_back and f.f_back.f_back and f.f_back.f_back.f_code == f.f_code:
raise TailRecurseException(args, kwargs)
else:
while 1:
try:
return g(*args, **kwargs)
except TailRecurseException, e:
args = e.args
kwargs = e.kwargs
func.__doc__ = g.__doc__
return func
#fibtail
@tail_call_optimized
def fibt(n, current=0, next=1):
if n == 0:
return current
else:
return fibt(n - 1, next, current+next)
さて、メモ化を使用してフィボナッチ関数を高速化する方法があります。再帰の限界を押し上げる方法があります。両方を行う方法がわかりません。
#fibboth
@memoize
@tail_call_optimized
def fibb(n, current=0, next=1):
if n == 0:
return current
else:
return fibb(n - 1, next, current+next)
fibboth 0.00103717311766
fibtail 0.274269805675
fibmemo 0.000844891605448
fibnorm 0.0242854266612
140未満の入力で機能するように見えるデコレータを組み合わせてみましたが、それを超える瞬間RuntimeError: maximum recursion depth exceeded
が発生します。それはほとんど@tail_call_optimized
失敗のようなものです。
「なに…?」
質問:
- これらのデコレータを組み合わせる方法はありますか? もしそうなら、どのように?
- 組み合わせると、デコレータがより小さな入力に対して機能しているように見えるのはなぜですか?