Skip to content

Instantly share code, notes, and snippets.

@x
Created April 21, 2015 20:05
Show Gist options
  • Save x/6004163a013fb5f23dc4 to your computer and use it in GitHub Desktop.
Save x/6004163a013fb5f23dc4 to your computer and use it in GitHub Desktop.
Joy of Clojure Chapter 5 Notes

Persistence

(def ds (into-array [:willie :barnabas :adam])) ;; not a vector
(seq ds)
;=> (:willie :barnabas :adam)

(def ds1 (aset ds 1 :quentin))
(seq ds1)
;=> (:willie :quentin :adam)
(seq ds)
;=> (:willie :quentin :adam)

(def ds [:willie :barnabas :adam])
(def ds1 (replace {:barnabas :quentin} ds))
ds
;=> [:willie :barnabas :adam]
ds1
;=> [:willie :quentin :adam]

A sequential collection is one that holds a series of values without reordering them. As such, it’s one of three broad categories of collection types along with sets and maps.

'(foo bar baz)
[:foo :bar :baz]

A sequence is a sequential collection that represents a series of values that may or may not exist yet. They may be values from a concrete collection or values that are computed as necessary. A sequence may also be empty.

'(foo bar baz)
((fn slow-nums
  ([]
    (slow-nums 0))
  ([n]
    (Thread/sleep 1000)
	(cons n (lazy-seq (slow-nums (inc n)))))))
(map str [1 2 3])
(map str {:a 1 :b 2 :c 3})

Clojure has a simple API called seq for navigating collections.

(seq '(1 2 3))
(seq [1 2 3])
(seq #{1 2 3})
(seq #{1 :a 2 :b 3 :c})

;; hangs forever in repl when trying to print
(seq ((fn slow-nums
        ([]
          (slow-nums 0))
        ([n]
          (Thread/sleep 1000)
      	(cons n (lazy-seq (slow-nums (inc n))))))))

;; doesn't hang forever
(def foo (seq ((fn slow-nums
                 ([]
                   (slow-nums 0))
                 ([n]
                   (Thread/sleep 1000)
      	         (cons n (lazy-seq (slow-nums (inc n)))))))))

(doseq [i '(1 2 3)] (println i))
(doseq [i [1 2 3]] (println i))
(doseq [i #{1 2 3}] (println i))
(doseq [i #{1 :a 2 :b 3 :c}] (println i))
(doseq [i ((fn slow-nums
             ([]
               (slow-nums 0))
             ([n]
               (Thread/sleep 1000)
           	   (cons n (lazy-seq (slow-nums (inc n)))))))]
  (println i))

Cons-cell like structures and lazyness

(def foo (cons :a (cons :b (cons :c '()))))

(defn get-c [] (println "hi") :c)
(def foo (cons :a (cons :b (cons (get-c) '()))))

(def foo (cons :a (cons :b (lazy-seq (cons (get-c) '())))))

for vs doseq vs map

(defn get-c [] (println "hi") :c)
(def foo (cons :a (cons :b (lazy-seq (cons (get-c) '())))))

(def for-foo (for [val foo] (str foo)))
(def do-foo (doseq [val foo] (str foo)))

(def foo (cons :a (cons :b (lazy-seq (cons (get-c) '())))))
(def map-foo (map str foo))
(do-all map-foo)

equality

(= [1 2 3] '(1 2 3))
;=> true

(= [1 2 3] #{1 2 3})
;=> false

(= (map identity [1 2 3]) (map identity #{1 2 3}))
;=true

Big O

Everything that's usually O(1) is O(n * log_32(n))

this seems bad but...

log_32(1,000,000) < 4

Vector

[:a :b :c]
;=> [:a :b :c]

;; merging
(into [:a :b :c] [:e :f])
;=> [:a :b :c :e :f]

;; adding
(conj [:a :b :c] :e)
;=> [:a :b :c :e]

(assoc [:a :b :c] 1 :d)
;=> [:a :d :c]

;; accessing
(get [:a :b :c] 1)
;=> :b

(nth [:a :b :c] 1)
;=> :b

([:a :b :c] 1)
;=> :b

Efficient at

  • adding and removing from the right
  • accessing or changing things by index
  • walking backwards???

rseq (walking backwards)

; walking forwards (left to right)
(seq [:a :b :c])
;=> '(:a :b :c)

; walking backwards (right to left)
(rseq [:a :b :c])
;=> '(:c :b :a)

; how it's "constant time"
(class [:a :b :c])
;=> clojure.lang.PersistentVector

(class (rseq [:a :b :c]))
;=> clojure.lang.APersistentVector$RSeq

;; some times that make me question everything
(time (def blah (into #{} (vec (range 10000000)))))
#=> "Elapsed time: 6041.905022 msecs"
(time (def blah (into #{} (rseq (vec (range 10000000))))))
#=> "Elapsed time: 9835.961141 msecs"
(time (def blah (into #{} (seq (vec (range 10000000))))))
#=> "Elapsed time: 9968.160465 msecs"

Basic functions

  • conj (adding): adds onto the collection in the most efficient way
(conj [:a :b :c] :e)
;=> [:a :b :c :e]

(conj '(:a :b :c) :e)
;=> (:e :a :b :c)

(conj {:a 1} [:b 2])
{:a 1 :b 2}

(conj #{:a :b :c} :d)
#{:a :b :c :d}

key-value functions

  • get: gets a value from a collection using the key
(get {:a 1 :b 2} :a)
;=> 1

(get [:a :b :c] 1)
;=> :b

;; interestingly this also works on sets
(get #{:a :b :c} :a)
;=> :a
  • get-in: recursive get
(get-in {:a {:b {:c 1}}} [:a :b :c])
;=> 1

(get-in [[0 [1] 2] 3] [0 1 0])
;=> 1

(get-in {:a [1 2]} [:a 1])
;=> 1
  • assoc: inserts a value into a collection, overwriting existing value at that key
(assoc {:a 1 :b 2} :a 3)
;=> {:a 3 :b 2}

(assoc [:a :b :c] 1 :d)
;=> [:a :d :c]
  • assoc-in: recursive assoc
(assoc-in [[0 [1] 2] 3] [0 1 0] 4)
[[0 [4] 2] 3]

(assoc-in {:a {:b {:c 1}}} [:a :b :c] 4)
;=> {:a {:b {:c 4}}}
  • update-in: assoc-in with a merge function
(update-in {:a 1} [:a] + 2)a
;=> {:a 3}

You can do really cool stuff with update-in and fnil

(defn count-stuff [coll]
  (reduce (fn [m val]
            (update-in m [val] (fnil inc 0)))
  		{}
  		coll))
(count-stuff [:a :b :b :c :a :c :c :c :b :d])
;=> {:d 1, :c 4, :b 3, :a 2}

Vectors as stacks

(def my-stack [1 2 3])
(peek my-stack)

;=> 3
(pop my-stack)

;=> [1 2]
(conj my-stack 4)
;=> [1 2 3 4]

(+ (peek my-stack) (peek (pop my-stack)))
;=> 5

Subvec

(subvec [:a :b :c :d :e] 1 4)
[:b :c :d]

Sets

They're just sets

(set [1 2 3 4])
;=> #{1 2 3 4}

#{1 2 3 4}
;=> #{1 2 3 4}

(contains? 3)
;=> true

(#{1 2 3 4} 3)
;=> 3

(filter #{1 2 3 4} [1 2 6 3 2 34 56 7 4 3])
;=> (1 2 3 2 4 3)

(use 'clojure.set)
(intersection #{1 2 3} #{2 3 4})
;=> #{3 2}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment