Collections¶
Lists, vectors, maps and sets are values. Every function below returns a new collection and leaves its arguments alone, so a snapshot you took earlier never changes.
Literals are the usual way to build one; the functions are for when you build one at run time:
user=> [1 2 3]
[1.0 2.0 3.0]
user=> {:name "Bolt" :speed 0.5}
{:name "Bolt", :speed 0.5}
user=> #{1 2 3}
#{1.0 2.0 3.0}
user=> (list 1 2 3)
(1.0 2.0 3.0)
A set literal with a repeated value is a syntax error; use set to remove
duplicates from an existing collection.
list, vector, hash-map and hash-set do the same from values you computed.
vec and set convert a collection, and range builds a list of numbers:
user=> (vec (range 3))
[0.0 1.0 2.0]
user=> (set [1 1 2])
#{1.0 2.0}
user=> (range 5)
(0.0 1.0 2.0 3.0 4.0)
user=> (range 0 10 3)
(0.0 3.0 6.0 9.0)
range needs an end: nothing is lazy here, so every element is built at once.
Reading¶
user=> (count [1 2 3])
3.0
user=> (first [1 2 3])
1.0
user=> (rest [1 2 3])
(2.0 3.0)
user=> (nth [10 20 30] 1)
20.0
user=> (get {:a 1} :a)
1.0
user=> (get {:a 1} :b :missing)
:missing
user=> (keys {:a 1 :b 2})
(:a :b)
user=> (vals {:a 1 :b 2})
(1.0 2.0)
first, second and last return nil for an empty collection; rest
returns an empty list and next returns nil. contains? asks about a key for
a map or set, and about an index for a vector:
user=> (contains? {:a 1} :a)
true
user=> (contains? [10 20] 1)
true
user=> (empty? [])
true
user=> (seq [])
nil
user=> (seq [1])
(1.0)
seq turns any collection into a list, or nil when it is empty, which is why
it works as an emptiness test in if.
get-in reads a path, and peek looks at the end of a vector or the front of a
list:
user=> (get-in {:arm {:angle 45}} [:arm :angle])
45.0
user=> (peek [1 2 3])
3.0
user=> (peek (list 1 2 3))
1.0
Changing¶
conj adds to the natural end: the back of a vector, the front of a list, or a
[key value] pair to a map. cons always adds to the front of a sequence:
user=> (conj [1 2] 3)
[1.0 2.0 3.0]
user=> (conj (list 1 2) 3)
(3.0 1.0 2.0)
user=> (conj {:a 1} [:b 2])
{:a 1.0, :b 2.0}
user=> (cons 1 [2])
(1.0 2.0)
user=> (pop [1 2 3])
[1.0 2.0]
assoc sets a key or index, update changes a value through a function, and
update-in does the same at a path. dissoc and disj remove:
user=> (assoc {:a 1} :b 2)
{:a 1.0, :b 2.0}
user=> (assoc [1 2] 0 9)
[9.0 2.0]
user=> (update {:power 0.5} :power * 2)
{:power 1.0}
user=> (update-in {:arm {:angle 30}} [:arm :angle] + 15)
{:arm {:angle 45.0}}
user=> (assoc-in {} [:a :b] 1)
{:a {:b 1.0}}
user=> (dissoc {:a 1 :b 2} :a)
{:b 2.0}
user=> (disj #{1 2} 1)
#{2.0}
assoc-in and update-in create missing levels on the way down, which makes
them handy for nested configuration maps.
into pours one collection into another and merge combines maps:
user=> (into [1] [2 3])
[1.0 2.0 3.0]
user=> (into {} [[:a 1]])
{:a 1.0}
user=> (merge {:a 1} {:b 2 :a 3})
{:a 3.0, :b 2.0}
Sequences¶
map, filter, reduce, some and every? take a function, and the usual
anonymous function is fn:
user=> (map inc [1 2 3])
(2.0 3.0 4.0)
user=> (filter even? [1 2 3 4])
(2.0 4.0)
user=> (reduce + [1 2 3])
6.0
user=> (reduce + 10 [1 2 3])
16.0
user=> (some even? [1 2 3])
true
user=> (every? pos? [1 2 3])
true
concat and reverse do what they say:
user=> (concat [1] [2 3])
(1.0 2.0 3.0)
user=> (reverse [1 2 3])
(3.0 2.0 1.0)
Because keywords are functions, map can pull one key out of every map. This
is the usual way to summarise robot state:
user=> (map :name [{:name "Bolt"} {:name "Dash"}])
("Bolt" "Dash")
Predicates¶
list?, seq?, vector?, map?, set? and coll? test the type of a value:
user=> (vector? [1])
true
user=> (seq? [1])
false
user=> (coll? {:a 1})
true
Lists and vectors¶
A list and a vector print differently, (1 2 3) and [1 2 3], and both work
as sequences. They differ in where conj adds, as shown above, and in that a
vector can be indexed with nth or by calling it.