2

有没有办法在 Clojure 中定义一个自动进行尾调用优化的函数?

例如

(defrecur fact [x]
    (if (= x 1)
        1
        (* x (fact (dec x)))))

将在内部翻译为:

(defn fact [x]
    (loop [n x f 1]
        (if (= n 1)
            f
            (recur (dec n) (* f n)))))

你能告诉我这样的东西是否已经存在吗?

4

3 回答 3

3

最简洁的答案是不”。

稍长一点的答案是,Clojure 被故意设计为需要明确指示需要尾调用优化的位置,因为 JVM 本身并不支持它。

顺便说一句,您可以recur不使用loop,因此不再需要输入,例如:

(defn func [x]
  (if (= x 1000000)
    x
    (recur (inc x))))

更新,4 月 29 日:

Chris Frisz 一直在与 Dan Friedman 一起开展Clojure TCO研究项目,虽然目前没有人声称它是“答案”,但该项目既有趣又充满希望。Chris 最近就这个项目进行了一次非正式的讨论,并将其发布在他的博客上

于 2012-04-18T15:43:26.110 回答
1

这个决定背后的指导原则之一是让特殊部分看起来很特别。这样一来,尾部调用在哪里使用和在哪里不使用就很明显了。这是一个经过深思熟虑的设计决定,有些人对此有强烈的看法,尽管在实践中我很少看到 recur 在惯用的 Clojure 中使用,所以在实践中这不是一个常见的问题。

于 2012-04-18T20:32:41.633 回答
1

据我所知,在 Clojure 中没有自动生成尾递归的方法。

有一些使用递归而不使用循环的函数的示例.. recur 可以在不溢出堆栈的情况下工作。那是因为这些函数已经被仔细编写为使用惰性序列。

这是一个用手写函数替换 flatten 的示例。这个例子来自http://yyhh.org/blog/2011/05/my-solutions-first-50-problems-4clojure-com

(fn flt [coll]
  (let [l (first coll) r (next coll)]
    (concat 
      (if (sequential? l)
        (flt l)
        [l])
      (when (sequential? r)
        (flt r)))))
于 2012-04-18T15:45:18.903 回答