Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Recursion 导致堆栈溢出的递归函数_Recursion_Clojure_Primes_Lazy Evaluation_Lazy Sequences - Fatal编程技术网

Recursion 导致堆栈溢出的递归函数

Recursion 导致堆栈溢出的递归函数,recursion,clojure,primes,lazy-evaluation,lazy-sequences,Recursion,Clojure,Primes,Lazy Evaluation,Lazy Sequences,我试图编写一个简单的筛选函数来计算clojure中的素数。我见过关于编写一个有效的筛选函数的问题,但我还没有到那个程度。现在我只是想写一个非常简单(而且很慢)的筛子。以下是我的想法: (defn sieve [potentials primes] (if-let [p (first potentials)] (recur (filter #(not= (mod % p) 0) potentials) (conj primes p)) primes)) 对于小范围,它可以正常

我试图编写一个简单的筛选函数来计算clojure中的素数。我见过关于编写一个有效的筛选函数的问题,但我还没有到那个程度。现在我只是想写一个非常简单(而且很慢)的筛子。以下是我的想法:

(defn sieve [potentials primes]
  (if-let [p (first potentials)]
    (recur (filter #(not= (mod % p) 0) potentials) (conj primes p))
    primes))
对于小范围,它可以正常工作,但对于大范围,它会导致堆栈溢出:

user=> (sieve (range 2 30) [])
[2 3 5 7 11 13 17 19 23 29]
user=> (sieve (range 2 15000) [])
java.lang.StackOverflowError (NO_SOURCE_FILE:0)

我认为通过使用
recur
这将是一个不消耗堆栈的循环构造?我错过了什么?

你被
过滤器的懒惰所打击。将
中的
(filter…
更改为
(doall(filter…)

更深入的解释是:


filter
的调用返回一个惰性seq,它根据需要实现已过滤seq的实际元素。如前所述,您的代码在
filter
上堆叠
filter
filter
上,在每次迭代中添加一个级别的
filter
ing;在某一点上,它爆炸了。解决方案是在每次迭代时强制整个结果,以便下一次迭代将对完全实现的seq进行过滤,并返回完全实现的seq,而不是添加额外的延迟seq处理层;这就是
doall
所做的。

从算法上讲,问题是在没有更多目的时继续过滤。尽早停止可以实现递归深度的二次减少(
sqrt(n)
vs.
n
):


16000次运行正常(只执行30次迭代,而不是1862次),160000次运行正常。即使在没有
doall

+1的情况下运行速度也会提高5%,因为在问题标题中出现堆栈溢出;对我有用。您正在使用什么版本的Clojure,在什么平台上使用什么JVM?你能在没有溢出的情况下运行
(范围2 15000)
?Ubuntu 9.10,Java 1.6.0_15,Clojure 1.2.0的最新快照是的,我在15000时溢出。你能在不溢出的情况下运行一百万吗?标题应该是“导致堆栈溢出的非递归函数”。谢谢!这解决了我的问题。很好的解释。你有什么想法如何找到答案?也许像macroexpand之类的东西?我想说,看看堆栈跟踪。一堆
clojure.lang.LazySeq
方法调用将很好地表明问题与懒惰有关。
(defn sieve [potentials primes]    
  (if-let [p (first potentials)]
      (if (> (* p p) (last potentials))
        (concat primes potentials)
        (recur (filter (fn [n] (not= (mod n p) 0)) potentials)
               (conj primes p)))
    primes))