Python优化尾部递归吗?

Python优化尾部递归吗?,python,recursion,stack,stack-overflow,tail-recursion,Python,Recursion,Stack,Stack Overflow,Tail Recursion,我有以下一段代码失败,错误如下: 运行时错误:超过最大递归深度 我试图重写它以允许尾部递归优化(TCO)。我相信如果发生了TCO,这段代码应该是成功的 def trisum(n, csum): if n == 0: return csum else: return trisum(n - 1, csum + n) print(trisum(1000, 0)) 我是否应该得出这样的结论:Python不具备任何类型的TCO,还是我只需要对其进行不同的

我有以下一段代码失败,错误如下:

运行时错误:超过最大递归深度

我试图重写它以允许尾部递归优化(TCO)。我相信如果发生了TCO,这段代码应该是成功的

def trisum(n, csum):
    if n == 0:
        return csum
    else:
        return trisum(n - 1, csum + n)

print(trisum(1000, 0))
我是否应该得出这样的结论:Python不具备任何类型的TCO,还是我只需要对其进行不同的定义?

不支持,也可能永远不会支持基于该主题的尾部调用优化


我听过这样的观点,即由于它如何修改堆栈跟踪,调试变得更加困难。

不,而且它永远不会,因为它更喜欢能够进行正确的跟踪:

(2009-04-22)

(2009-04-27)

您可以通过以下转换手动消除递归:

>>> def trisum(n, csum):
...     while True:                     # Change recursion to a while loop
...         if n == 0:
...             return csum
...         n, csum = n - 1, csum + n   # Update parameters instead of tail recursion

>>> trisum(1000,0)
500500
Guido这个词在

我最近在我的Python历史博客上发表了一篇关于 Python的功能特性。关于不支持尾部的旁注 递归消除(TRE)立即引发了关于 可惜的是Python没有做到这一点,包括到 其他人最近的博客文章试图“证明”可以添加TRE 要轻松地使用Python。所以让我来捍卫我的立场(我不这么认为) 想要英语中的TRE)。如果你想要一个简短的答案,那很简单 非音速的。答案很长:


我发布了一个执行尾部调用优化(处理尾部递归和延续传递样式)的模块:

Python中尾部递归的优化 经常有人声称尾部递归不适合Pythonic的编码方式,人们不应该关心如何将其嵌入到循环中。我不想和你争论 这一观点;然而,有时我喜欢尝试或实施新的想法 作为尾部递归函数,而不是由于各种原因使用循环(重点是 想法,而不是在过程中,有二十个短功能在我的屏幕上在同一时间 时间,而不仅仅是三个“Pythonic”函数,在交互式会话中工作,而不是编辑我的代码,等等)

在Python中优化尾部递归实际上相当容易。虽然据说这是不可能的 或者非常棘手,我认为它可以通过优雅、简短和通用的解决方案来实现;我甚至 认为这些解决方案中的大多数都不使用Python特性,而应该使用其他特性。 干净的lambda表达式与非常标准的循环一起工作,可以实现快速、高效和高效的循环 用于实现尾部递归优化的完全可用的工具

为了个人方便,我编写了一个实现这种优化的小模块 通过两种不同的方式。我想在这里讨论我的两个主要功能

干净的方法:修改Y组合符 这是众所周知的;它允许以递归方式使用lambda函数 但它本身不允许在循环中嵌入递归调用。兰姆达 光靠微积分是做不到这一点的。然而,在Y组合中有一点变化 可以保护要实际计算的递归调用。因此,评估可能会推迟

下面是Y组合器的著名表达式:

lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args)))
只要稍作改动,我就能得到:

lambda f: (lambda x: x(x))(lambda y: f(lambda *args: lambda: y(y)(*args)))
函数f现在返回一个执行 非常相同的调用,但由于它返回了它,因此可以稍后从外部进行评估

我的代码是:

def bet(func):
    b = (lambda f: (lambda x: x(x))(lambda y:
          f(lambda *args: lambda: y(y)(*args))))(func)
    def wrapper(*args):
        out = b(*args)
        while callable(out):
            out = out()
        return out
    return wrapper
该功能可按以下方式使用:;下面是两个带有尾部递归的示例 阶乘和斐波那契的版本:

>>> from recursion import *
>>> fac = bet( lambda f: lambda n, a: a if not n else f(n-1,a*n) )
>>> fac(5,1)
120
>>> fibo = bet( lambda f: lambda n,p,q: p if not n else f(n-1,q,p+q) )
>>> fibo(10,0,1)
55
显然,递归深度不再是问题:

>>> bet( lambda f: lambda n: 42 if not n else f(n-1) )(50000)
42
这当然是函数的唯一真正目的

只有一件事不能用这个优化来完成:它不能用在 尾部递归函数求值到另一个函数(这来自事实 所有可调用的返回对象都作为进一步的递归调用进行处理 没有区别)。因为我通常不需要这样的功能,我很高兴 使用上面的代码。然而,为了提供一个更通用的模块,我认为 为了找到解决此问题的一些解决方法(请参阅下一节),请再做一点

关于这个过程的速度(这不是真正的问题),它发生了 很好,;尾部递归函数的计算速度甚至比使用 以下代码使用更简单的表达式:

def bet1(func):
    def wrapper(*args):
        out = func(lambda *x: lambda: x)(*args)
        while callable(out):
            out = func(lambda *x: lambda: x)(*out())
        return out
    return wrapper
我认为计算一个表达式,即使很复杂,也比 计算几个简单表达式,第二个版本就是这样。 我没有在我的模块中保留这个新函数,而且我看不出在什么情况下它会出现 可以使用,而不是“官方”的

带例外的连续传递样式 这里有一个更一般的函数;它能够处理所有尾部递归函数, 包括那些返回其他函数的函数。递归调用是从 通过使用异常返回的其他值。此解决方案比 前一个;一个更快的代码可能是通过使用一些特殊的 在主循环中检测到作为“标志”的值,但我不喜欢 使用特殊值或内部关键字。有一些有趣的解释 使用异常的方法:如果Python不喜欢尾部递归调用,则会出现异常 应该在发生尾部递归调用时引发,并且python方法将 捕捉异常以找到干净的解决方案,这实际上是什么 发生在这里

class _RecursiveCall(Exception):
  def __init__(self, *args):
    self.args = args
def _recursiveCallback(*args):
  raise _RecursiveCall(*args)
def bet0(func):
    def wrapper(*args):
        while True:
          try:
            return func(_recursiveCallback)(*args)
          except _RecursiveCall as e:
            args = e.args
    return wrapper
现在所有功能都可以使用了。在下面的示例中,
f(n)
计算为 n的任何正值的单位函数:

>>> f = bet0( lambda f: lambda n: (lambda x: x) if not n else f(n-1) )
>>> f(5)(42)
42
当然,有人可能会争辩说,例外情况并非有意用于 重定向解释器(作为一种
goto
语句,或者更确切地说是一种 我必须承认这一点。但是,再一次, 我觉得使用
try
的想法很有趣,其中一行是
return
import sys
sys.setrecursionlimit(5500000)
print("recursion limit:%d " % (sys.getrecursionlimit()))
pip install astrologic
from astrologic import no_recursion

counter = 0

@no_recursion
def recursion():
    global counter
    counter += 1
    if counter != 10000000:
        return recursion()
    return counter

print(recursion())