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/

-- 
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

Reply via email to