簡體   English   中英

Python中的尾遞歸優化裝飾器

[英]Tail-Recursion Optimization Decorator in Python

我最近一直在學習Scala,所以我用Python編寫了一些遞歸。

而且我發現Python中沒有尾遞歸優化。

然后,我發現了一個魔術器(?)裝飾器 ,該裝飾器似乎可以優化尾遞歸。

它解決了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.

沒有尾調用優化,您的堆棧如下所示:

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循環的下一個迭代。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM