在下面(Clojure)SO问题:我自己的插入功能作为练习
接受的答案说:
用recur调用替换你的递归调用,因为写入它会遇到堆栈溢出
(defn foo [stuff]
(dostuff ... )
(foo (rest stuff)))
Run Code Online (Sandbox Code Playgroud)
变为:
(defn foo [stuff]
(dostuff ...)
(recur (rest stuff)))
Run Code Online (Sandbox Code Playgroud)
避免吹栈.
这可能是一个愚蠢的问题,但我想知道为什么递归调用foo不会被recur自动替换?
此外,我采取了另一个SO示例并写了这个(没有故意使用cond,只是为了尝试一下):
(defn is-member [elem ilist]
(if (empty? ilist)
false
(if (= elem (first ilist))
true
(is-member elem (rest ilist)))))
Run Code Online (Sandbox Code Playgroud)
而且我想知道我是否应该用recur替换对is-member的调用(这似乎也有效).
是否存在递归的情况,特别是不应该使用复发?
如果你有一个尾递归方法,几乎没有理由不使用recur,虽然除非你在一个对性能敏感的代码区域,否则它不会有任何区别.
我认为基本的论点是recur明确表明函数是否是尾递归的; 所有尾递归函数都使用recur,所有函数recur都是尾递归的(如果你尝试recur从非尾部位置使用,编译器会抱怨.)所以这是一个美学决定.
recur还有助于区分Clojure与将在所有尾调用上执行TCO的语言,如Scheme; Clojure无法有效地执行此操作,因为它的函数被编译为Java函数,而JVM不支持它.通过制作recur一个特例,希望没有夸大的期望.
我不认为为什么编译器无法recur为你插入有任何技术原因,如果它是这样设计的,但也许有人会纠正我.
我问Rich Hickey,他和他的推理基本上(我解释)
"使特殊情况看起来很特别"
大多数时候,他没有看到特殊情况下的纸张价值,并让人们想知道为什么如果在某些事情发生变化并且编译器无法保证优化的情况下打击堆栈.不合时宜的只是尝试保持语言简单的设计决策之一