【问题标题】:Tail-Recursion Optimization Decorator in PythonPython 中的尾递归优化装饰器
【发布时间】:2014-12-11 07:57:24
【问题描述】:

我最近一直在学习Scala,所以我用Python写了一些递归。

我发现 Python 中没有尾递归优化。

然后我found a magic(?) decorator 似乎优化了尾递归。

它解决了RuntimeError: maximum recursion depth exceeded

但我不明白这段代码的工作原理和方式。

有人能解释一下这段代码的魔力吗?

代码:

# This program shows off a python decorator(
# which implements tail call optimization. It
# does this by throwing an exception if it is 
# its own grandparent, and catching such 
# exceptions to recall the stack.

import sys

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 its 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

@tail_call_optimized
def factorial(n, acc=1):
  "calculate a factorial"
  if n == 0:
    return acc
  return factorial(n-1, n*acc)

print factorial(10000)
# prints a big, big number,
# but doesn't hit the recursion limit.

@tail_call_optimized
def fib(i, current = 0, next = 1):
  if i == 0:
    return current
  else:
    return fib(i - 1, next, current + next)

print fib(10000)
# also prints a big number,
# but doesn't hit the recursion limit.

【问题讨论】:

  • 哪一部分你不明白?您是否已经知道尾调用优化是什么以及它是如何工作的——并且您想了解装饰器?还是栈?还是例外?就目前而言,这个问题太宽泛了,无法在这里做出有意义的回答。
  • 您是否阅读并理解了 cmets?
  • 感谢您的评论。是我的问题没有问清楚,没有仔细阅读代码中的注释。
  • 如果你关心 Python 中的尾递归,你应该看看 tco 模块;查看演示文稿:baruchel.github.io/python/2015/11/07/…

标签: python recursion tail-call-optimization


【解决方案1】:

如果没有尾调用优化,您的堆栈如下所示:

factorial(10000)
factorial(9999)
factorial(9998)
factorial(9997)
factorial(9996)
...

并不断增长,直到您达到sys.getrecursionlimit() 呼叫(然后是kaboom)。

带有尾调用优化:

factorial(10000,1)
factorial(9999,10000) <-- f.f_back.f_back.f_code = f.f_code? nope
factorial(9998,99990000) <-- f.f_back.f_back.f_code = f.f_code? yes, raise excn.

异常使装饰器进入其while循环的下一次迭代。

【讨论】:

    猜你喜欢
    • 2020-01-05
    • 2012-11-15
    • 1970-01-01
    • 1970-01-01
    • 2010-12-03
    • 2014-06-23
    • 1970-01-01
    • 1970-01-01
    • 2021-05-30
    相关资源
    最近更新 更多