Clojure中的自动TCO

时间:2012-04-18 15:38:17

标签: function clojure automation tail-recursion tail-call-optimization

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

e.g。

(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)))))

你能否告诉我这样的事情是否已经存在?

3 个答案:

答案 0 :(得分:2)

简短的回答是“不”。

稍微长一点的答案是Clojure被故意设计为需要明确指示需要Tail Call Optimization的地方,因为JVM本身不支持它。

顺便说一下,您可以在没有recur的情况下使用loop,因此不再需要输入内容,例如:

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

更新,4月29日:

克里斯·弗里斯(Chris Frisz)一直在和丹·弗里德曼(Dan Friedman)一起研究Clojure TCO研究项目,虽然目前还没有人声称它是“答案”,但这个项目既有趣又有前途。克里斯最近就这个项目进行了非正式的讨论,he's posted it on his blog

答案 1 :(得分:1)

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

有一些函数使用递归而不使用循环。重复工作而不会溢出堆栈。这是因为已经仔细编写了这些函数以使用延迟序列。

以下是使用手写功能替换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)))))

答案 2 :(得分:1)

这个决定背后的指导原则之一是使特殊部分看起来特别。这样很明显尾部调用正在使用中,而不是在哪里。这是一个深思熟虑的设计决定,有些人在实践中有强烈的意见我很少看到在idomatic Clojure中反复使用所以在实践中这不是一个常见的问题。