I don't have a complete answer, but...
Using your first version (cons and lazy-seq):
user> (take 25 primes)
(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97)
Using your second version (range):
user> (take 25 primes)
(2 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 37 41 43 47 53 59 61 67 71)
Notice that the range version has every odd number right up until 31,
then it switches to starting to get the primes correct from there on.
So now I'm suspicious. A quick look at the source for (range) and
note it's doing chunking, with 32-item chunks:
(defn range
...
([start end step]
(lazy-seq
(let [b (chunk-buffer 32)
comp (if (pos? step) < >)]
(loop [i start]
(if (and (< (count b) 32)
(comp i end))
(do
(chunk-append b i)
(recur (+ i step)))
(chunk-cons (chunk b)
(when (comp i end)
(range i end step)))))))))
Quite a numerical coincidence... So is it possible that the
difference is in how the lazy sequences are being called on to produce
their next item? Perhaps range, since it has a next one available and
pre-calculated already, offers its up more willingly than your
hand-rolled lazy-seq? Figuring the details of that out is beyond my
limited capabilities at the moment, but it seems worth investigating.
Finally, for interest in figuring out exactly what the two different
behaviours are:
(def right-nums (cons 2 (lazy-seq (map inc right-nums))))
(def right-primes (cons (first right-nums)
(lazy-seq (->>
(rest right-nums)
(remove
(fn [x]
(let [dividors (take-while #(<= (* % %) x)
right-primes)]
(some #(= 0 (rem x %)) dividors))))))))
(def wrong-nums (drop 2 (range)))
(def wrong-primes (cons (first wrong-nums)
(lazy-seq (->>
(rest wrong-nums)
(remove
(fn [x]
(let [dividors (take-while #(<= (* % %) x)
wrong-primes)]
(some #(= 0 (rem x %)) dividors))))))))
(def wrong-answers
(filter (fn [x]
(not (some #(= x %)
(take-while #(<= % x) right-primes))))
wrong-primes))
user> (take 2 wrong-answers)
(9 15)
user> (take 3 wrong-answers)
(9 15 21)
user> (take 5 wrong-answers)
(9 15 21 25 27)
user> (take 6 wrong-answers)
<... much time passes, I get bored, Ctrl-C ...>
; Evaluation aborted.
Hope this helps?!
The functions range, rest, and remove are chunk-aware, so the range-based version of primes consumes a bunch of numbers before its changes become self-visible. Other functions, such as iterate, are not chunked, so the results are visible to primes sooner.
In most domains it is rare to have definitions with *this* kind of self-reference. When you do have it, your best bet is to take explicit control over the laziness by using recur and/or lazy-seq directly. The example below (simplified from contrib) demonstrates this approach to primes:
(def primes
(concat
[2]
(let [primes-from
(fn primes-from
[n]
(if (some #(zero? (rem n %))
(take-while #(<= (* % %) n) primes))
(recur (+ n 2))
(lazy-seq (cons n (primes-from (+ n 2))))))]
(primes-from 2))))
This approach also saves memory over having a separate nums collection--nums is fully (and unnecessarily) realized in memory in the implementations below.
Hope this helps,
Stu
> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clo...@googlegroups.com
> Note that posts from new members are moderated - please be patient with your first post.
> To unsubscribe from this group, send email to
> clojure+u...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
(def primes
(concat
[2]
(lazy-seq
(let [primes-from
(fn primes-from
[n]
(if (some #(zero? (rem n %))
(take-while #(<= (* % %) n) primes))
(recur (+ n 2))
(lazy-seq (cons n (primes-from (+ n 2))))))]
(primes-from 3)))))
Stu
As a workaround, eliminate the lazy intermediary "nums" sequence, e.g.
(def primes
(concat
[2]
(lazy-seq
(let [primes-from
(fn primes-from
[n]
(if (some #(zero? (rem n %))
(take-while #(<= (* % %) n) primes))
(recur (+ n 2))
(lazy-seq (cons n (primes-from (+ n 2))))))]
(primes-from 3)))))
Stu