{:draft ["true"]}
Clojure's concept of seq
applies to a surpriingly wide range of data types.
In particular, Clojure provides facility of lazy evaluation.
A lazy expression is an expression that is not evaluated until it is needed.
Clojure's support for lazy sequences allows one to build representation of infinite sequences using lazy list
construction.
First, let's look at an example of a normal sequence.
(defn slow-list-builder [n]
(if (zero? n)
nil
(do (Thread/sleep 500)
(println "conj" n)
(conj (slow-list-builder (dec n)) n))))
(def my-list (slow-list-builder 20))
(time (take 3 my-list))
We say that slow-list-builder
is an eager function because it has built the entire
list from 1
to n
regardless of how many elements are required.
(conj xs element)
, we protect it with the lazy-seq
form:(lazy-seq (conj <list-expr> element))
This will ensure that <list-expr>
is evaluated only when more elements from it are needed by some
computation.
(defn lazy-list-builder [n]
(if (zero? n)
nil
(do (Thread/sleep 500)
(println (str ">>" n "<<"))
(lazy-seq (cons n (lazy-list-builder (dec n)))))))
(time (def my-list (lazy-list-builder 10)))
Note:
(take 3 my-list)
(take 4 my-list)
(take 3 (for [i (range 10000)]
(do (println (str ">>" i "<<"))
i)))
doall
: forces lazy seq to materialize¶(doall <seq-expression>)
#"\d+.\d"
.(re-matches pattern string)
nil
if pattern does not match string
.If the pattern contains groups:
If the pattern does not have groups:
string
if pattern matches the entire string.(re-matches #"(\d+)(.\d+)?" "hello")
(re-matches #"(\d+)(\.\d+)?" "123")
(re-matches #"(\d+)(\.\d+)?" "3.1415")
(re-matches #"\d+\.\d+" "3.1415")
(re-find pattern string)
pattern
is used to match non-overlapping substrings in string
.(re-seq #"\d+\.\d+" "Gold 6.7, Oil 8.9")
(re-seq #"(\d+)(\.\d+)" "Gold 6.7, Oil 8.9")
(clojure.java.io/reader <filename>)
converts filename to a reader.(line-seq <reader>)
constructs a lazy sequence of lines from a reader.;
; Let's import clojure.java.io with a shorter alias.
;
(require '[clojure.java.io :as io])
(let [r (io/reader "weather_madrid.csv")]
(clojure.pprint/pprint (take 3 (line-seq r))))
;
; (range n) is a finite lazy sequence
;
(range 10)
;
; Let's time how long it takes to construct all one million elements
;
(time (take 3 (doall (range 1e6))))
;
; Now, we just take the three elements
;
(time (take 3 (range 1e6)))
;
; (range) returns an infinite sequence
;
(take 3 (range))
(repeat n <expr>)
: sequence with copies of <expr>
(repeat <expr>)
: infinite sequence(repeat 10 :a)
(take 3 (repeat :a))
(iterate f x)
: returns an infinite with:
(take 5 (iterate inc 0))
(take 5 (iterate (partial * 2) 1))
(cycle seq)
: infinite sequence that cycles through the seq
(take 20 (cycle ["." "-" "o"]))
(interpose separator seq)
separator
between elements in seq
.(interpose " | " (take 4 (cycle ["math" "physics" "chemistry"])))
;
; Rember the `(apply f ...)` can be used to pass elements of sequence
; as arguments to a function.
;
(apply str (interpose " | " (take 4 (cycle ["math" "physics" "chemistry"]))))
(interleave seq1 seq2 ...)
(interleave [:a :b :c] [1 2 3 4 5 6])
;
; Using interleave like interpose
; Oops, there is an extra element to be removed by (but-last)
;
(interleave (take 4 (cycle ["math" "physics" "chemistry"]))
(repeat " | "))
Threading macros are a different syntax to express computation that can be described as a pipeline of transformations.
f g h
x ---> x1 ---> x2 --->
We call x
, x1
, x2
the threaded value.
(defn f [x] (inc x))
(defn g [x] (/ x 2.))
(defn h [x] (str "result = " x))
(-> 100 f g h)
But the threading macro can support multi-input functions as well. There are three variations:
->
prepend the threaded value to the begining of the argument list.->>
appends the threaded value to the end of the argument list.as->
binds the threaded value to a symbol for the duration of the pipeline.(-> 100
(inc)
(/ 2.)
(str " is the result."))
(->> 100
(inc)
(- 2)
(str "Result is "))
(as-> 100 $
(inc $)
(- $ 2)
(if (even? $)
(str "Result is even: " $)
(str "Result is odd: " $)))
(->> (range)
(filter even?)
(map #(format "%02d" %))
(interpose "|")
(take 50)
(apply str))
(every? pred seq)
(some pred seq)
(every? even? [1 2 3 4])
(every? even? (map (partial * 2) [1 2 3 4]))
(some even? [1 2 3 4])
(some even? (map #(+ (* 2 %) 1) [1 2 3 4]))
(defn divisible? [m n]
(zero? (rem m n)))
(divisible? 10 5)
(divisible? 10 3)
(defn prime? [m]
(not (some (partial divisible? m) (range 2 m))))
(prime? 10)
(prime? 13)
(defn next-prime [n]
(first (filter prime? (iterate inc (inc n)))))
(next-prime 17)