2009年03月31日
:
  1. >     function foo (n)
  2. >>       if n> 0 then return foo(n - 1) end
  3. >>     end
  4. > foo(100)
  5. > foo(1000)
  6. > foo(10000)
  7. > foo(100000)
  8. > foo(1000000)

如果函数最后一句是return g(...)这样的形式,将会把这句解释为goto g(x),因为这里除了对g函数调用,再没有别的事做,也不需要保存堆栈里调用函数的信息。因此上面即使调用很多次也没有出现堆栈溢出的问题,把上面的代码转换为:

:
  1. >>> def foo(n):
  2. ...     if n>0: return foo(n-1)
  3. ...
  4. >>> foo(100)
  5. >>> foo(1000)
  6. ...
  7.   File "<stdin>", line 2, in foo
  8.   File "</stdin><stdin>", line 2, in foo
  9.   File "</stdin><stdin>", line 2, in foo
  10. RuntimeError: maximum recursion depth exceeded

很快出就现了堆栈溢出的问题,在这里表现为达到了递归调用的最大限制。自身没有实现尾部调用优化,不过也可以通过实现一个decorator办法来实现:

:
  1. #!/usr/bin/env python2.4
  2. # This program shows off a decorator(
  3. # which implements tail call optimization. It
  4. # does this by throwing an exception if it is
  5. # it's own grandparent, and catching such
  6. # exceptions to recall the stack.
  7.  
  8. import sys
  9.  
  10. class TailRecurseException:
  11.   def __init__(self, args, kwargs):
  12.     self.args = args
  13.     self.kwargs = kwargs
  14.  
  15. def tail_call_optimized(g):
  16.   """
  17.  This function decorates a function with tail call
  18.  optimization. It does this by throwing an exception
  19.  if it is it's own grandparent, and catching such
  20.  exceptions to fake the tail call optimization.
  21.  
  22.  This function fails if the decorated
  23.  function recurses in a non-tail context.
  24.  """
  25.   def func(*args, **kwargs):
  26.     f = sys._getframe()
  27.     if f.f_back and f.f_back.f_back
  28.         and f.f_back.f_back.f_code == f.f_code:
  29.       raise TailRecurseException(args, kwargs)
  30.     else:
  31.       while 1:
  32.         try:
  33.           return g(*args, **kwargs)
  34.         except TailRecurseException, e:
  35.           args = e.args
  36.           kwargs = e.kwargs
  37.   func.__doc__ = g.__doc__
  38.   return func
  39.  
  40. @tail_call_optimized
  41. def factorial(n, acc=1):
  42.   "calculate a factorial"
  43.   if n == 0:
  44.     return acc
  45.   return factorial(n-1, n*acc)
  46.  
  47. print factorial(10000)
  48. # prints a big, big number,
  49. # but doesn't hit the recursion limit.
  50.  
  51. @tail_call_optimized
  52. def fib(i, current = 0, next = 1):
  53.   if i == 0:
  54.     return current
  55.   else:
  56.     return fib(i - 1, next, current + next)
  57.  
  58. print fib(10000)
  59. # also prints a big number,
  60. # but doesn't hit the recursion limit.

代码来自http://code.activestate.com/recipes/474088/

在这段代码里tail_call_optimized是一个decorator,在执行factorial函数前,这个decorator先执行,tail_call_optimized中通过sys._getframe()这个方法会返回一个frame对象,包含了堆栈顶部的信息,当发现当前调用是一个递归调用:

:
  1. if f.f_back and f.f_back.f_back
  2.         and f.f_back.f_back.f_code == f.f_code:

则抛出一个异常,下面的代码则截获异常,继续执行,这样就避免了堆栈的使用,很巧妙的一种方式。

-dev的邮件列表里,有人曾经做过一个的尾调用优化的补丁,不过Guido拒绝了这个补丁:

I'm not interested in adding this to the official release.

One reason is that if an exception happens in such a tail-recursive
call, the stack trace will be confusing.

Another reason is that I don't think it's a good idea to try to
encourage a Scheme-ish "solve everything with recursion" programming
style in .

But feel free to maintain this as an independent modification, a la
Stackless -- I'm sure there are people who would like to try this
out.

--Guido van Rossum (home page: http://www..org/~guido/)

标签 :

随机日志

3 楼了已经

  • shellex写于09年05月04日

    唉。真是的。加入尾递归支持又不意味着大家都会do every thing with recursion...
    多个选择多好...

  • shellex写于09年05月04日

    非得hack下。真是的。

  • xiongharry写于09年10月21日

    黑色主题,配深色字体,看起来真累。

发表评论

在下面加入你的评论,或者 trackback 从你的博客站点。 订阅本文的评论。

:

:

:

«
»