【问题标题】:Detecting Infinite recursion in Python or dynamic languages检测 Python 或动态语言中的无限递归
【发布时间】:2011-01-31 05:35:22
【问题描述】:

最近我尝试用 GCC 编译类似这样的程序:

int f(int i){
    if(i<0){ return 0;}
    return f(i-1);
f(100000);

它运行得很好。当我检查堆栈帧时,编译器优化了程序以仅使用一帧,只需跳回函数的开头并仅将参数替换为 f。而且 - 编译器甚至没有在优化模式下运行。

现在,当我在 Python 中尝试同样的事情时 - 我遇到了最大递归墙(或者如果我将递归深度设置得太高,可能会导致堆栈溢出)。

像 python 这样的动态语言有没有办法利用这些很好的优化? 也许可以使用编译器而不是解释器来完成这项工作?

只是好奇!

【问题讨论】:

  • 好问题。在将静态语言与动态语言进行比较时,我完全忘记了一些东西。

标签: python gcc compiler-construction


【解决方案1】:

它与它是一种动态语言或它被解释的事实无关。 CPython 只是没有实现 Tail Recursion 优化。您可能会发现 JPython 等可以。

【讨论】:

  • 真的吗?我认为编译的优点之一是寻找这样的优化策略?我同意程序是编译还是解释都没有关系,但优化是编译优势的有力候选者,一个是你可以寻找尾递归。
  • @WeNeed 即使该语言被解释,它仍然可以经历优化阶段,它不能像 C 这样的语言那么长。
  • 这是否意味着它对解释器来说更好更容易,如果你事先告诉它需要做一些优化,比如在 F# 和“rec”关键字中?跨度>
【解决方案2】:

当我检查堆栈帧时,编译器优化程序以仅使用一帧,只需跳回函数的开头并仅替换 f 的参数。

您所描述的称为“尾递归”。有些编译器/解释器支持它,有些不支持。事实上,大多数人都没有。正如您所注意到的,gcc 确实如此。事实上,尾递归是 Scheme 编程语言规范的一部分,因此所有 Scheme 编译器/解释器必须支持尾递归。另一方面,Java 和 Python(以及大多数其他语言,我敢打赌)等语言的编译器不进行尾递归。

像 python 这样的动态语言有没有办法利用这些很好的优化?

你的意思是,现在,还是你用更抽象的术语问?抽象地说,是的!动态语言绝对有可能利用尾递归(例如,Scheme)。但具体来说,不,CPython(规范的 Python 解释器)没有标志或其他参数来启用尾递归。

【讨论】:

  • 函数式程序是否已经在寻找尾递归问题,因为使用堆栈的性质如此之多,而诸如 C#、Java 之类的命令主要使用堆存储。奇怪的是,64 位 .net 不会破坏堆栈,尽管 32 位版本会。我知道在 F# 中,解决方法是通过在语法 (rec) 中声明它来使函数递归。
【解决方案3】:

您所说的优化称为尾调用消除 - 递归调用展开为迭代循环。

已经对此进行了一些讨论,但目前的情况是不会添加它,至少不会添加到 cpython 本身。请参阅Guido's blog entry 进行一些讨论。

但是,确实存在 some decorators 操纵函数来执行此优化。它们通常只节省空间,而不是时间(事实上,它们通常更慢)

【讨论】:

  • 无堆栈python怎么样?是否实现了尾调用优化?
  • @drozzy:不完全确定。我认为可能。我已经在 pypy 上尝试过(包括无堆栈版本),但它看起来不像实现它(至少目前)
猜你喜欢
  • 2012-03-26
  • 1970-01-01
  • 2015-06-10
  • 1970-01-01
  • 1970-01-01
  • 2021-12-23
  • 1970-01-01
  • 2012-02-20
  • 2014-08-17
相关资源
最近更新 更多