有没有办法在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)))))
你能否告诉我这样的事情是否已经存在?
答案 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中反复使用所以在实践中这不是一个常见的问题。