在clojure中使用recur时溢出

在clojure中使用recur时溢出,clojure,primes,tail-recursion,Clojure,Primes,Tail Recursion,我在clojure中有一个简单的素数计算器(一个低效的算法,但我现在只是想了解Recurr的行为)。代码是: (defn divisible [x,y] (= 0 (mod x y))) (defn naive-primes [primes candidates] (if (seq candidates) (recur (conj primes (first candidates)) (remove (fn [x] (divisible x (

我在clojure中有一个简单的素数计算器(一个低效的算法,但我现在只是想了解Recurr的行为)。代码是:

(defn divisible [x,y] (= 0 (mod x y)))

(defn naive-primes [primes candidates] 
  (if (seq candidates)
      (recur  (conj primes (first candidates)) 
              (remove (fn [x] (divisible x (first candidates))) candidates))
      primes)
)
只要我不想找到太多的数字,这就行了。比如说

(print (sort (naive-primes [] (range 2 2000))))
工作。对于任何需要更多递归的操作,我会得到一个溢出错误

    (print (sort (naive-primes [] (range 2 20000))))

这是行不通的。一般来说,无论我是使用recur还是再次调用naive prime而不尝试TCO,似乎都没有任何区别。为什么在使用recur时,大型递归会出现错误?

recur
始终使用尾部递归,无论是循环还是函数头。问题是调用
删除
remove
调用
first
从底层seq获取元素,并检查该元素是否有效。如果基础seq是通过调用
remove
创建的,那么您首先会得到另一个调用
。如果在同一个序列上调用
remove
20000次,则调用
first
需要调用
first
20000次,并且所有调用都不能是尾部递归的。因此,堆栈溢出错误

    (print (sort (naive-primes [] (range 2 20000))))

(remove…
更改为
(doall(remove…)
可以解决此问题,因为它可以防止
remove
调用的无限堆叠(每个调用都会立即完全应用并返回具体的seq,而不是惰性seq)。我认为这种方法一次只能在内存中保存一个候选列表,尽管我对此并不乐观。如果是这样的话,它的空间效率并不是太低,而且一点测试表明它实际上并没有慢多少。

递归是否需要循环来获得尾部递归?我在代码中没有看到循环。我想回答这个问题,但我还在学习Clojure。你的代码在Clojure 1.2.1和1.3中对我有效。我最终得到的唯一错误是在查找高达200000个素数时出现的
OutOfMemoryError
。@octopusgrabbus,不,recur也可以以这种方式使用(仅在函数体中)。请参阅。@HansEngel在1.3中的repl上运行,我在查找高达200000的素数时遇到堆栈溢出错误。下面是我的解释。我很抱歉在发布之前没有找到这个。我的问题类似于感谢。如果没有你的帮助,我要花很长时间才能弄明白。虽然道尔对我来说有点不可思议,但这非常有帮助!