On Oct 24, 5:44 pm, Chouser <[EMAIL PROTECTED]> wrote:
> I ran into a problem with "take" today (thanks to wwmorgan on IRC for
> helping steer me away from blaming "filter").
>
> My actual code had to do with computing a subset of primes, but let's
> take a simpler example.
>
> (defn painful-seq []
>   (lazy-cat
>     [0 1 2]
>     (comment lots of hard work here)
>     (list 99)))
>
> user=> (lazy-cat [0 1 2] (painful-list))
> (0 1 2 99)
>
> This is meant to represent 3 easy-to-compute values, followed by the
> hard-to-compute number 99.  So now if we take only those first three,
> we avoid having to compute 99, right?
>
> user=> (take 3 (painful-seq))
> (0 1 2)
>
> Well, it looks good.  But let's "instrument" the code, as the cool
> kids put it:
>
> (defn painful-seq []
>   (lazy-cat
>     [0 1 2]
>     (println "hurt me plenty")
>     (list 99)))
>
> user=> (take 3 (painful-seq))
> (0 1 hurt me plenty
> 2)
>
> Whoops.  The return value is correct as we saw earlier, but before 2
> gets returned, we apparently go on and compute that next expensive
> value.  This is despite the fact that nobody even wants it.
>
> So I wrote up an new "take" function that seems to fix this:
>
> user=> (my-take 3 (painful-seq))
> (0 1 2)
>
> Of course if you actually want the fourth value, there's nothing I can
> do for you.
>
> (0 1 hurt me plenty
> 2 99)
>
> Here's "my-take" renamed to "take" for easy patching of boot.clj:

Thanks for the report - I've made take less eager (SVN 1078), but it's
important that everyone understand what is lazy about lazy seqs and
what isn't.

first and rest are lazy, but the creation of the seq itself is not.
This is to support direct conditional existence testing of seqs and
all of the idioms built upon that.

Sometimes you may want to delay some of the work of sequence creation
into first or rest. For instance, even with the change above, if the
"hurt me plenty" call were first, you'd get it simply from creating
but not consuming any take.

In some cases you can use delays to push off some expensive work into
the first/rest call rather than the seq-creating body:

(defn eseq []
  (let [ev (do (println "expensive") [1 2 3])]
    (lazy-cons (first ev) (rest ev))))

(def x (take 2 (eseq))) ;not consumed, but still does init work
-> expensive
   #=(var user/x)

;use delay to move work into first/rest:

(defn eseq []
  (let [ev (delay (do (println "expensive") [1 2 3]))]
    (lazy-cons (first (force ev)) (rest (force ev)))))

(def x (take 2 (eseq))) ;not consumed, expensive work delayed
-> #=(var user/x)

(first x)
-> expensive
   1

Rich

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to