Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clojure lazy sequence usage

I'm having trouble understanding how one creates a lazy sequence in Clojure.

The documentation for the macro isn't at all clear to me:

Usage: (lazy-seq & body) Takes a body of expressions that returns an ISeq or nil, and yields a Seqable object that will invoke the body only the first time seq is called, and will cache the result and return it on all subsequent seq calls.

All the examples that I've seen, seem to do something like the following:

; return everything in the sequence starting at idx n (defn myseq-after-n [n]   (...) )  (def my-lazy-seq   (lazy-seq (conj [init-value] (myseq-after-n 2))) ) 

So, the first thing I don't get is, since lazy-seq is outside the call to conj, how does it prevent conj from generating an infinite sequence at evaluation?

My second question is, do lazy sequence definitions always take this general form?

like image 915
Kricket Avatar asked Feb 14 '11 12:02

Kricket


People also ask

What is a lazy sequence in Clojure?

Overview. Clojure is not a lazy language. However, Clojure supports lazily evaluated sequences. This means that sequence elements are not available ahead of time and produced as the result of a computation.

What is lazy sequence?

Lazy sequences are regular sequences where each item is computed on demand rather than up front. For example, consider this array of numbers: let numbers = Array(1... 100000) That will hold 100,000 numbers.

Does seq do in Clojure?

The 'doseq' statement is similar to the 'for each' statement which is found in many other programming languages. The doseq statement is basically used to iterate over a sequence.

How do you iterate in Clojure?

One way to translate an imperative for loop to Clojure is to use the for macro. The above function will return all the numbers from 0 to 9 incremented by 1. However, it appears you simply want to iterate over a sequential collection and use each item.


1 Answers

A lazy-seq call just executes the body once the first time it is accessed, then caches and returns the same result whenever it is called again in the future.

If you want to use this to build long (or even infinite) sequences, then you need to recursively nest other lazy-seq calls in the returned sequence. Here's about the simplest case I can think of:

(defn ints-from [n]   (cons n (lazy-seq (ints-from (inc n)))))  (take 10 (ints-from 7)) => (7 8 9 10 11 12 13 14 15 16) 

Any (ints-from n) call produces a sequence starting with n, followed by a lazy sequence of (ints-from (inc n)). It's an infinite list, but that's not a problem because the lazy-seq ensures that (int-from (inc n)) only gets called when it is needed. You could try exactly the same code without the lazy-seq and you'd get a StackOverflowError very quickly.

lazy-seq is just one of many possible ways to create lazy sequences, and it often isn't the most convenient. The following are some other interesting/useful ways to create lazy sequences:

; range is an easy way to get an infinite lazy sequence of integers, starting with zero      (take 10 (range)) => (0 1 2 3 4 5 6 7 8 9)  ; map produces lazy sequences, so the following is lazy  (take 10 (map #(* % %) (range))) => (0 1 4 9 16 25 36 49 64 81)  ; iterate is a good way of making infinite sequenes of the form x, f(x), f(f(x)).....  (take 10 (iterate (partial * 2) 1)) => (1 2 4 8 16 32 64 128 256 512) 
like image 82
mikera Avatar answered Oct 06 '22 10:10

mikera