A more succinct test case: user=> (def escher-seq (lazy-seq (lazy-seq (if (seq escher-seq) nil (list 42))))) #'user/escher-seq user=> escher-seq (42)
thus escher-seq is not empty because it is empty :-) On Wed, Nov 28, 2012 at 4:42 PM, Christophe Grand <christo...@cgrand.net>wrote: > Hi Ulrich, > > Wow, you got me scratching my head because your code should not even work. > The problem I see with it is that your recursion has no base case. You > have to know that 2 is prime to boot the algorithm. Eg > > (def primes > (cons 2 > > (filter > (fn isprime[n] > (every? > #(pos? (mod n %)) > (take-while #(<=(*%%)n) primes))) > (iterate inc 3)))) > > > So now we have two behaviours to explain: > 1/ why it works with iterate > 2/ why it doesn't work with range > The difference comes from range returning a chunked seq (which explain the > batched processing you rightly identified) > > Both have a definitive reentrant smell. > > 1/ Let's look at the code for LazySeq > https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/LazySeq.java#L59especially > the seq and sval methods. > So calling seq (and thus realizing the first prime) on a yet unrealized > primes sequence causes at some points to evaluate > (every? > #(pos? (mod n %)) > (take-while #(<=(*%%)n) primes)) > > which by the nature of every? is going to realize at least one item of > primes. But we are exactly trying to compute it! > From the implementation point of view, we are in the first call to seq, in > the while loop. f has been cleared bu the first call to sval (which set sv) > and sv cleared before entering the while and s has still its default value: > null. > Thus a recursive call to seq causes is going to see sv as null and returns > s which is null! > So the primes into the take-while is considered empty! Thus every? returns > true and 2 is considered prime! By luck. > > A sentinel or a flag could solve the problem. > > 2/ This is the same problem exacerbated by the fact that we are producing > a chunked seq so the recursive call sees an empty prime for the first 30 > items. > > However if you swap iterate by range in my version (which is correct, > having a base case) you still have a reentrance bug > > ser=> (def primes > (cons 2 > > (filter > (fn isprime[n] > (every? > #(pos? (mod n %)) > (take-while #(<=(* % %)n) primes))) > (drop 3 (range))))) > > #'user/primes > user=> (take 50 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 73 > 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 157 163 167 173 179 > 181 191 193 197) > > Christophe > > > > > On Wed, Nov 28, 2012 at 2:29 PM, Ulrich <umuel...@c007.de> wrote: > >> Hello, >> >> I tried to define the sequence of primes via corecursion, >> with trial divisions by primes p with p*p<=n, >> finally arriving at following code. >> >> (def primes >> (filter >> (fn isprime[n] >> (every? >> #(pos? (mod n %)) >> (take-while #(<=(*%%)n) primes) >> ) >> ) >> (iterate inc 2) >> ) >> ) >> >> Seems there's nothing wrong with that, >> it's just the algorithm straightly set down >> in a functional way. And it works correctly >> as one can quickly confirm by "(take 50 primes)". >> >> But if replacing "(iterate inc 2)" with "(drop 2 (range))", >> we get strange behaviour, "primes" then consists of all!! >> numbers from 2 until 30 followed by primes only from 31 on. >> >> Being relatively new to clojure, I find this >> very irritating and a potential cause of bad bugs. >> And hope that this is not a bug in clojure itself >> or even bad language design, but rather >> some basic misunderstanding by me. >> >> So I analyzed it a bit, and it seems, that >> "range" triggers? execution of "drop" and "filter" >> in batches of 32 numbers, and "primes" in "take-while" >> is not updated during such a batch. While using >> "iterate" instead updates "primes" each step. >> >> Can someone more into clojure than me please correct and >> better explain internal reasons of this strange behaviour. >> How could one know the "batch size" of more complicated >> expressions? And how could "range" be wrapped to yield >> numbers one by one? >> >> Thanks, Ulrich. >> >> >> -- >> You received this message because you are subscribed to the Google >> Groups "Clojure" group. >> To post to this group, send email to clojure@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+unsubscr...@googlegroups.com >> For more options, visit this group at >> http://groups.google.com/group/clojure?hl=en > > > > > -- > On Clojure http://clj-me.cgrand.net/ > Clojure Programming http://clojurebook.com > Training, Consulting & Contracting http://lambdanext.eu/ > > -- On Clojure http://clj-me.cgrand.net/ Clojure Programming http://clojurebook.com Training, Consulting & Contracting http://lambdanext.eu/ -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@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+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en