lazy-seq

added
1.0

ns
clojure.core

type
macro

(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. See also - realized?

                ;; The following defines a lazy-seq of all positive numbers.  Note that 
;; the lazy-seq allows us to make a recursive call in a safe way because
;; the call does not happen immediately but instead creates a closure.

user=> (defn positive-numbers 
\t([] (positive-numbers 1))
\t([n] (lazy-seq (cons n (positive-numbers (inc n))))))
#'user/positive-numbers

user=> (take 5 (positive-numbers))
(1 2 3 4 5)


            
                ;; A lazy-seq of Fibonacci numbers (fn = fn-1 + fn-2)
;; The producer function takes exactly two parameters
;; (because we need the last 2 elements to produce a new one)
user=> (defn fib 
         ([]
           (fib 1 1))
         ([a b]
           (lazy-seq (cons a (fib b (+ a b))))))

user=> (take 5 (fib))
(1 1 2 3 5)
            
                ;; It might be easier to think about the producer function as a function
;; that, given element n, produces element n+1 via a recursive call to 
;; itself, wrapped with lazy-seq to delay its execution
;; We might also provide no-argument version of the function that calls 
;; itself for the first element(s) of the sequence being generated.
;; => variant of fibonaci with a no-arg version and using cons first:
(defn sum-last-2 
   ([] (sum-last-2 1 2)) 
   ([n m] (cons n (lazy-seq (sum-last-2 m (+ n m))))))

user=> (take 6 (sum-last-2))
(1 2 3 5 8 13)
            
                ;; An example combining lazy sequences with higher order functions
;; Generate prime numbers using trial division.
;; Note that the starting set of sieved numbers should be
;; the set of integers starting with 2 i.e., (iterate inc 2) 
(defn sieve [s]
  (cons (first s)
        (lazy-seq (sieve (filter #(not= 0 (mod % (first s)))
                                 (rest s))))))

user=> (take 20 (sieve (iterate inc 2)))
(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71)


Sadly (nth (sieve (iterate inc 2)) 10000) results in StackOverflowError ;(
            
                ;; Other examples on this page are little too eager to produce the head of collection
;; right away. lazy-seq was introduced to make it possible to postpone any computation
;; until data is needed.
;; While it is not relevant for these simple examples, it could be important
;; for real apps where producing each element is expensive.

;; Here is a demonstration.
;; Let's define a function that prints mysqr when it's called
(defn mysqr [n]
  (println "mysqr")
  (* n n))
;; => #'user/mysqr

;; Now function squares that is adopted from positive-numbers example above
;; Note that lazy-seq is inside of cons
(defn squares
  ([n] (cons (mysqr n) (lazy-seq (squares (inc n))))))
;; => #'user/squares

(def sqrs (squares 1))
;; => mysqr  <-- NOTE THAT mysqr WAS CALLED WHEN WE SIMPLY REQUESTED COLLECTION
;; => #'user/sqrs

(take 1 sqrs)
;; => (1) <-- HERE WE ARE GETTING FIRST ELEMENT THAT WAS CALCULATED BEFORE

;; Now let's redefine 'squares' by wrapping its entire body in lazy-seq:
(defn squares
  ([n] (lazy-seq (cons (mysqr n) (squares (inc n))))))
;; => #'user/squares

;; And when we request the collection:
(def sqrs (squares 1))
;; => #'user/sqrs
;; NOTE THAT mysqr WAS NOT CALLED HERE

(take 1 sqrs)
;; => mysqr <- AND HERE mysqr IS CALLED WHEN FIRST ELEMENT IS ACTUALLY REQUESTED
;; => (1)
            
                ;; Compare recursive functions and lazy sequences
;; generate valid parenthesis combinations
;; valid paren combinations for 1 paren - ()
;; valid paren combinations for 2 paren - ()(),(())
;; valid paren combinations for 3 paren - ()()(),()(()),(())(),(()()),((()))

;; given ith item, generate (i+1)th item
(defn next-parens
    [xs]
    (set (mapcat (juxt
                  #(str "()" %)
                  #(str % "()")
                  #(str "(" % ")"))
                 xs)))

;; recursive function to get n paren combinations
;; combinations are recursively calculated on the stack
(defn parens-nth-item
    [n]
    (if (= 0 n)
      #{""}
      (next-parens (parens-nth-item (dec n)))))
user=> (parens-nth-item 3)
#{"(()())" "((()))" "()()()" "()(())" "(())()"}

;; lazy function to get sequence of paren combinations
;; combinations are lazily calculated on the heap
(defn parens-sequence
    [xs]
    (lazy-seq (cons xs (parens-sequence (next-parens xs)))))

user=> (take 3 (parens-sequence #{""}))
(#{"()"} #{"(())" "()()"} #{"(()())" "((()))" "()()()" "()(())" "(())()"})
            
                ; Create a finite-length lazy seq
(defn finite-lazy-builder [values]
  (lazy-seq
    ; We need the when-let so the lazy-seq will terminate
    (when-let [ss (seq values)]
      (cons (first values)
            (finite-lazy-builder (next values))))))

(println (finite-lazy-builder [1 2 3 4 5] ))
;=> (1 2 3 4 5)