lazy-seq problems

10 views
Skip to first unread message

jon

unread,
Aug 19, 2009, 10:01:10 PM8/19/09
to Clojure
Hi,
Two problems I'd like to ask about.. (using clojure 1.0)

(1) The following code seems to work correctly
( generates Hamming numbers -- see
http://en.wikipedia.org/wiki/Haskell_(programming_language)#More_complex_examples
)
but...
;--------------------------------------------------------------------
(defn merge-seqs [xs ys]
(lazy-seq
(let [x (first xs) y (first ys)]
(cond (= x y) (cons x (merge-seqs (rest xs) (rest ys)))
(< x y) (cons x (merge-seqs (rest xs) ys))
:else (cons y (merge-seqs xs (rest ys)))))))

(def hamming
(lazy-seq
(cons 1 (merge-seqs (map #(* 2 %) hamming) (merge-seqs (map #(*
3 %) hamming) (map #(* 5 %) hamming))))))

(prn (nth hamming 5000))
;--------------------------------------------------------------------
...if I define merge-seqs using destructured binding of the function
arguments I get a java.lang.StackOverflowError.
;--------------------------------------------------------------------
(defn merge-seqs [[x & xs :as x-xs] [y & ys :as y-ys]]
(lazy-seq
(cond (= x y) (cons x (merge-seqs xs ys))
(< x y) (cons x (merge-seqs xs y-ys))
:else (cons y (merge-seqs x-xs ys)))))
;--------------------------------------------------------------------
Can that be avoided/fixed easily?


(2) Here's a simplified function in a similar vein but using
interleave for which the docs say "Returns a lazy seq" so I expected
it to work.. however this also gives me a
java.lang.StackOverflowError.
;--------------------------------------------------------------------
(def squares
(lazy-seq
(cons 1 (interleave (map #(* 2 %) squares) (map #(* 2 %)
squares)))))

(prn (take 50 squares))
;--------------------------------------------------------------------
If I replace the usage of interleave with the following hackish
interleave2 that uses lazy-seq directly, it works as expected...
;--------------------------------------------------------------------
(defn interleave2 [& colls]
(lazy-seq
(when (not-any? nil? (map seq colls))
(concat (map first colls) (apply interleave2 (map rest
colls))))))

(def squares
(lazy-seq
(cons 1 (interleave2 (map #(* 2 %) squares) (map #(* 2 %)
squares)))))

(prn (take 50 squares))
;--------------------------------------------------------------------
Is this a bug?
Is it because the call to apply in the original version breaks the
laziness of concat?

(defn interleave [& colls]
(apply concat (apply map list colls)))

I haven't looked if there are any other api functions that should be
lazy and fail in the same way?
Cheers,
Jon.

Rich Hickey

unread,
Aug 20, 2009, 1:05:31 PM8/20/09
to Clojure


On Aug 19, 10:01 pm, jon <superuser...@googlemail.com> wrote:
> Hi,
> Two problems I'd like to ask about.. (using clojure 1.0)
>
> (1) The following code seems to work correctly
> ( generates Hamming numbers -- seehttp://en.wikipedia.org/wiki/Haskell_(programming_language)#More_comp...
Right now destructuring's & uses next. There has been some discussion
about a variant (&& ?) that would use rest. So, right now, no, using
destructuring in this case has you requiring the future in order to
determine the present.
Yes, now fixed in master - thanks for the report.

> Is it because the call to apply in the original version breaks the
> laziness of concat?
>
> (defn interleave [& colls]
>     (apply concat (apply map list colls)))
>

Right, apply is (and always will be) mostly lazy, but ahead by one. It
needs to be because it must "spread" its last argument, and so must
know when it is at the last argument by lookahead.

> I haven't looked if there are any other api functions that should be
> lazy and fail in the same way?

Possibly mapcat, although it currently only promises to apply + apply.

Rich
Reply all
Reply to author
Forward
0 new messages