Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to do destructured head/tail separation of lazy sequences in clojure?

Tags:

clojure

I see some examples that show we can get a nice head/tail destructuring of a sequence in clojure as follows:

(if-let [[x & xs] (seq coll)]

However I assume this won't work as desired for lazy sequences because this puts the values into a vector, which aren't lazy. I tried changing the vector form to a list form, and it gave me binding errors, quoted or not.

Without having binding like this, it seems that if I've got a lazy sequence where each element was a computationally-intensive equation of the previous element, I'd have to do that computation twice to get the head and tail as separate statements, right?

(let [head (first my-lazy-seq) ;; has to calculate the value of head.
      tail (rest my-lazy-seq)] ;; also has to calculate the value of head to prepare the rest of the sequence.

Is there any way around this, or am I making an incorrect assumption somewhere?

like image 697
Dax Fohl Avatar asked May 24 '13 01:05

Dax Fohl


2 Answers

user=> (let [[x & xs] (range)] [x (take 10 xs)])
[0 (1 2 3 4 5 6 7 8 9 10)]

xs is still a lazy seq, so you can use the destructuring with no problems. This will force the first element of xs, though. (Destructuring uses vector notation, but it doesn't necessarily use vectors under the covers.)

With respect to your second question: lazy seqs cache their results, so your second option would also work without extra recalculation. The head will only be calculated once.

like image 125
mange Avatar answered Oct 06 '22 17:10

mange


The binding vector [x & xs] isn't actually constructing a vector at runtime. It's just the notation used for destructuring into head & tail.

So it works fine on infinite sequences:

(if-let [[x & xs] (range)]
  (apply str x (take 9 xs)))
=> "0123456789"

The destructuring form is actually producing a lazy sequence in this case, which you can observe as follows:

(if-let [[x & xs :as my-seq] (range)]
  (class my-seq))
=> clojure.lang.LazySeq
like image 43
mikera Avatar answered Oct 06 '22 18:10

mikera