(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))
(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) '())))))
(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)
(= [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
Everything that's usually O(1) is O(n * log_32(n))
this seems bad but...
log_32(1,000,000) < 4
[: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???
; 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"
- 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}
- into (merging): takes two collections, returns a new one of the first's type
- second collection is treated as a seq
- internally uses conj
- basically just #(reduce conj %1 %2)
- source: https://github.com/clojure/clojure/blob/clojure-1.6.0/src/clj/clojure/core.clj#L6334-L6342
- 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}
(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 [:a :b :c :d :e] 1 4)
[:b :c :d]
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}