【问题标题】:What's wrong with this clojure prime seq?这个clojure prime seq有什么问题?
【发布时间】:2012-07-11 00:39:55
【问题描述】:
我不明白为什么这个惰性素数序列的定义会导致不终止。我得到的堆栈跟踪不是很有帮助(我对 clojure 的一个抱怨是钝的堆栈跟踪)。
(declare naturals is-prime? primes)
(defn naturals
([] (naturals 1))
([n] (lazy-seq (cons n (naturals (inc n))))))
(defn is-prime? [n]
(not-any? #(zero? (rem n %))
(take-while #(> n (* % %)) (primes))))
(defn primes
([] (lazy-seq (cons 2 (primes 3))))
([n] (let [m (first (filter is-prime? (naturals n)))]
(lazy-seq (cons m (primes (+ 2 m)))))))
(take 10 (primes)) ; this results in a stack overflow error
【问题讨论】:
标签:
clojure
lazy-sequences
【解决方案1】:
让我们开始执行primes,我们会神奇地实现一个seq,只是为了清楚起见。我会忽略naturals,因为它确实很懒:
> (magically-realise-seq (primes))
=> (magically-realise-seq (lazy-seq (cons 2 (primes 3))))
=> (cons 2 (primes 3))
=> (cons 2 (let [m (first (filter is-prime? (naturals 3)))]
(lazy-seq (cons m (primes (+ 2 3))))))
=> (cons 2 (let [m (first (filter
(fn [n]
(not-any? #(zero? (rem n %))
(take-while #(> n (* % %)) (primes)))))
(naturals 3)))]
(lazy-seq (cons m (primes (+ 2 3))))))
我已将is-prime? 替换为最后的fn — 您可以看到primes 将再次被调用,并且在take-while 提取元素时至少实现一次。这将导致循环。
【解决方案2】:
问题是要知道要计算“素数”函数,您使用的是“是素数吗?”函数,然后计算“是素数?”您正在使用“(素数)”的函数,因此堆栈溢出。
所以要计算“(primes 3)”,你需要计算“(first (filter is-prime?(naturals 3)))”,它会调用“(is-prime?1)” ,它调用“(primes)”,而后者又调用“(primes 3)”。换句话说,你正在做:
user=> (declare a b)
#'user/b
user=> (defn a [] (b))
#'user/a
user=> (defn b [] (a))
#'user/b
user=> (a)
StackOverflowError user/b (NO_SOURCE_FILE:1)
查看如何生成素数:Fast Prime Number Generation in Clojure
【解决方案3】:
我认为问题在于,您在 (primes) 已经构建之前尝试使用它。
像这样更改is-prime? 可以解决问题:
(defn is-prime? [n]
(not-any? #(zero? (rem n %))
(take-while #(>= n (* % %)) (next (naturals)))))
(请注意,我已将 > 更改为 >=,否则它会认为 4 是素数。它仍然说 1 是素数,这是不正确的,如果您使用 is-prime? 可能会导致问题其他地方。