14

Rich Hickey 和其他人提到 Clojure 不会从即将推出invokeDynamic的 JVM 7 或 8 计划中获得显着改进,但会从尾递归中看到性能提升。

尾递归是否会影响

(fn [...] (recur ...))

或者

(loop [...] (recur ...))

我不希望它们变得更快,因为编译器可能已经生成了循环结构。

4

1 回答 1

17

非你的例子会变得更快,因为如果你使用recur你已经告诉编译器你有一个尾递归函数,这允许编译器生成使用的字节码goto(就像一个正常的命令循环)

如果 JVM 获得尾调用优化,当然会有一些好处。

您不必再使用 recur (如果您不想),因此您可以编写这样的函数(尾递归函数)

(defn testfn [n] (when (not= 1000 n) (testfn n)))

现在 JVM 无法检测到尾递归。通过添加尾调用优化,JVM 能够看到上面的函数,就像你写的一样(因此获得命令式循环速度):

(defn testfn [n] (when (not= 1000 n) (recur n)))

所以这并不是一个很大的改进,但还有另一种情况,尾调用优化真的很棒。

如果您有相互调用的函数(有时甚至超过两个)并且不需要保留堆栈(尾递归),则 JVM 可以优化它们。现在这是不可能的,因为您无法告诉recur跳转到其他功能。这是一个例子。

(defn even [n] (if (zero? n) true (odd? (dec n)))
(defn odd  [n] (if (zero? n) false (even (dec n)))

如果您尝试使用大数字知道您会破坏堆栈,但使用尾调用优化您不会。

我只剩下很少的东西了。有一个名为的函数trampoline允许您已经这样做(编程风格稍有改变和一些开销)而不是解释,trampoline我将向您推荐一个完全可以做到这一点的博客:

http://pramode.net/clojure/2010/05/08/clojure-trampoline/

于 2010-11-29T18:47:34.020 回答