【问题标题】:Is there a way to explicitly write a elixir function to be tail call optimised?有没有办法显式编写一个长生不老药函数来优化尾调用?
【发布时间】:2015-11-28 14:11:37
【问题描述】:

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

"在非尾部位置重复出现错误", "recur 是功能性的,它在尾部位置的使用由编译器验证"

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

【问题讨论】:

  • 简单的答案是调用是 Erlang 和 Elixir 中的 TCO,除非您以防止它的方式对它们进行编码。基本上,如果你对函数的返回值做任何事情,例如将返回值加 1,那么它就不是 TCO,因为必须维护一个堆栈。

标签: recursion optimization clojure elixir tail-recursion


【解决方案1】:

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

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

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

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

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

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

(defn x [] (x))     ; <- StackOverflowError if called
(defn x [] (recur)) ; <- hangs if called

但是,如果您想强制执行,否则会失败,您必须这样做:

  • 编译前,例如。 G。静态代码分析使用一些标记来表示“期望的 TCO”
  • 在部署之前,例如。 G。带有会触发堆栈溢出的测试

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

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

【讨论】:

  • 你能确认一个尾调用是否必须只是一个函数调用而不是别的:即如果最后一个语句是funt(n),那是一个尾调用,如果它是1 + funct(n),那将不会进行尾部优化.
  • @DavidC 如果对返回值有任何计算(例如,将返回值加 1),则必须创建一个新的堆栈帧,并且调用不能是 TCO。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-14
  • 2016-02-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-16
相关资源
最近更新 更多