如何看待以及理解Python的这种尾递归优化
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何看待以及理解Python的这种尾递归优化相关的知识,希望对你有一定的参考价值。
参考技术A 把需要优化的函数的return改成yield,外面套个装饰器,就叫tail_call_opm。装饰器最内层的逻辑是while True:
try:
ret=next(ret)
except:
return ret
这个应该没有复用释放的空间…但刷题时换了这个就不爆栈了。返回闭包的话情况应该会更复杂一些。
不失为一个快速简单的办法。本回答被提问者采纳
一个很Cool的Idear->Python的尾递归优化
偶然在国外一个网站瞅到的,非常的酷,发出来共享一下。一般来说,Python和Java,C#一样是没有尾递归自动优化的能力的,递归调用受到调用栈长度的限制被广泛的诟病,但是这个狂人用一个匪夷所思的方法解决了这个问题并在Python上实现了,从此Python的递归调用再也不用受到调用栈长度的制约,太酷了。
首先我们还是从递归说起,之前我发过一篇 《浅谈递归过程以及递归的优化》其中用到了斐波那契数来作为例子.线性递归的算法由于太过一低效就被我们Pass掉了,我们先来看尾递过方式的调用:
2 if n<3:
3 return 1
4 else:
5 if n==c:
6 return b1+b2
7 else:
8 return Fib(n,b1=b2,b2=b1+b2,c=c+1)
9
这段程序我们来测试一下,调用 Fib(1001)结果:
>>> def Fib(n,b1=1,b2=1,c=3):
... if n<3:
... return 1
... else:
... if n==c:
... return b1+b2
... else:
... return Fib(n,b1=b2,b2=b1+b2,c=c+1)
...
>>> Fib(1001)
70330367711422815821835254877183549770181269836358732742604905087154537118196933579742249494562611733487750449241765991088186363265450223647106012053374121273867339111198139373125598767690091902245245323403501L
>>>
如果我们用Fib(1002),结果,茶几了,如下:
.....
File "<stdin>", line 8, in Fib
File "<stdin>", line 8, in Fib
File "<stdin>", line 8, in Fib
File "<stdin>", line 8, in Fib
File "<stdin>", line 8, in Fib
File "<stdin>", line 8, in Fib
RuntimeError: maximum recursion depth exceeded
>>>
好了,现在我们来尾递归优化
我们给刚才的Fib函数增加一个Decorator,如下:
1 @tail_call_optimized
3 if n<3:
4 return 1
5 else:
6 if n==c:
7 return b1+b2
8 else:
9 return Fib(n,b1=b2,b2=b1+b2,c=c+1)
恩,就是这个@tail_call_optimized的装饰器 ,这个装饰器使Python神奇的打破了调用栈的限制。
这下即使我们Fib(20000),也能在780ms跑出结果(780ms是以前博文提到那台2000元的上网本跑出来的结果)
不卖关子了,下面我们来看看这段神奇的代码:
2
3 class TailRecurseException:
4 def __init__(self, args, kwargs):
5 self.args = args
6 self.kwargs = kwargs
7
8 def tail_call_optimized(g):
9 """
10 This function decorates a function with tail call
11 optimization. It does this by throwing an exception
12 if it is it‘s own grandparent, and catching such
13 exceptions to fake the tail call optimization.
14
15 This function fails if the decorated
16 function recurses in a non-tail context.
17 """
18 def func(*args, **kwargs):
19 f = sys._getframe()
20 if f.f_back and f.f_back.f_back and f.f_back.f_back.f_code == f.f_code:
21 raise TailRecurseException(args, kwargs)
22 else:
23 while 1:
24 try:
25 return g(*args, **kwargs)
26 except TailRecurseException, e:
27 args = e.args
28 kwargs = e.kwargs
29 func.__doc__ = g.__doc__
30 return func
31
使用的方法前面已经展示了,令我感到大开眼界的是,作者用了抛出异常然后自己捕获的方式来打破调用栈的增长,简直是太匪夷所思了。而且效率问题,和直接尾递归Fib相比大概造成了五倍的时间开销。
最后很不可思议的,尾递归优化的目的达成了。
以上是关于如何看待以及理解Python的这种尾递归优化的主要内容,如果未能解决你的问题,请参考以下文章