有没有办法显式地编写一个 elixir 函数来优化尾调用?

san*_*ari 5 optimization recursion tail-recursion clojure elixir

例如,在 Clojure 中,您可以使用recur 特殊形式来明确使用非堆栈消耗递归调用的意图,并由编译器验证。正如它在 Clojure文档中所写:

“在尾部位置以外的重复是一个错误”,“recur 是功能性的,它在尾部位置的使用由编译器验证”

在 elixir 中,您可以将函数定义为一系列子句,并在它们之间进行递归调用。有没有办法确定定义的函数将进行尾调用优化?

D-s*_*ide 5

编译器不会优化函数。它优化了对该函数的调用。可能是调用了里面的同一个函数,也可能是调用了不同的函数,没关系。

...Clojure 的情况并非如此,在那里recur只能将执行返回到最新的“递归点”(无论是loop还是fn),这使得在没有“外部帮助”的情况下不可能进行相互递归,例如trampoline。它更像是一个创可贴而不是解决方案,但问题太大了,一个合适的解决方案需要太多。

是的,有一种方法可以确保它在 Elixir 中得到优化:它需要是一个实际的尾调用。就这样。

在计算机科学中,尾调用是作为过程的最终动作执行的子程序调用。

这可以通过目视检查代码轻松确定。

如果 Clojure 以这种方式工作,则以下两个定义将是等效的:

(defn x [] (x))     ; <- StackOverflowError if called
(defn x [] (recur)) ; <- hangs if called
Run Code Online (Sandbox Code Playgroud)

但是,如果您想强制执行此操作并否则失败,则必须执行以下任一操作:

  • 在编译之前,例如使用一些标记为“所需的 TCO”的静态代码分析
  • 在部署之前,例如使用会触发堆栈溢出的测试

后者似乎更实用,最简单的纯函数。

我还没有找到任何现有的解决方案。

  • @DavidC 如果对返回值有任何计算(例如,将返回值加1),则必须创建一个新的堆栈帧,并且调用不能是TCO。 (2认同)