lazy-seq和无限序列的堆栈溢出

时间:2015-03-16 09:23:12

标签: clojure lazy-evaluation lazy-sequences

我试图向非FP程序员展示惰性序列或惰性评估的重要性。我已经写了这个初级代的实现来展示这个概念:

(defn primes-gen [sieve]
  (if-not (empty? sieve)
    (let [prime (first sieve)]
      (cons prime
            (lazy-seq (primes-gen
                       (filter (fn [x]
                                 (not= 0 (mod x prime)))
                               (rest sieve))))))))

;;;;; --------- TO SHOW ABOUT THE LAZY-THINGS 
;; (take 400 (primes-gen (iterate inc 2)))
;; (take 400 (primes-gen (range 2 1000000000000N)))

但是,如果我给take更大的值,我会得到堆栈溢出异常。

堆栈是:

user> (pst)
StackOverflowError 

    clojure.core/range/fn--4269 (core.clj:2664)
    clojure.lang.LazySeq.sval (LazySeq.java:42)
    clojure.lang.LazySeq.seq (LazySeq.java:60)
    clojure.lang.RT.seq (RT.java:484)
    clojure.core/seq (core.clj:133)
    clojure.core/filter/fn--4226 (core.clj:2523)
    clojure.lang.LazySeq.sval (LazySeq.java:42)
    clojure.lang.LazySeq.seq (LazySeq.java:60)
    clojure.lang.RT.seq (RT.java:484)
    clojure.core/seq (core.clj:133)

似乎filter thunk正在积累。 但是如果(doall (filter ...那么我将无法处理无限序列,即(take 1000 (primes-gen (iterate inc 2)))将不再起作用。

这样做的正确方法是什么?

2 个答案:

答案 0 :(得分:3)

你的分析很明显:你过多地嵌套了过滤器。 你应该修改素数以获得两个args:已知素数和候选者的集合。 有关implementing the Erathostenes' sieve的其他一些想法,请参阅我的博客。

<强>更新 所以你将过滤器堆叠在过滤器上,当你想要获取新候选者时,堆栈太大了。

您必须将所有过滤器合并为一个(或合理数量的)过程。这很简单,因为过滤器非常均匀。所以我用一个包含已知素数的集合替换过滤器堆栈。

(defn primes-gen
 ([candidates] (primes-gen candidates []))
 ([candidates known-primes]
  (lazy-seq ; I prefer having the lazy-seq up here
    (when-first [prime candidates] ; little known macro
      (let [known-primes (conj known-primes prime)]
        (cons prime
          (primes-gen
            (drop-while (fn [n] (some #(zero? (mod n %)) known-primes)) candidates)
             known-primes)))))))

答案 1 :(得分:0)

其中一个可能的解决方案是在惰性seq中移动生成器函数。例如(取自here):

(def primes
  (concat
   [2 3 5 7]
   (lazy-seq
    (let [primes-from
          (fn primes-from [n [f & r]]
            (if (some #(zero? (rem n %))
                      (take-while #(<= (* % %) n) primes))
              (recur (+ n f) r)
              (lazy-seq (cons n (primes-from (+ n f) r)))))
          wheel (cycle [2 4 2 4 6 2 6 4 2 4 6 6 2 6  4  2
                        6 4 6 8 4 2 4 2 4 8 6 4 6 2  4  6
                        2 6 6 4 2 4 6 2 6 4 2 4 2 10 2 10])]
      (primes-from 11 wheel)))))