Clojure Functions Dealing With Sequences

I am finally through Chapter 3 of Programming Clojure. That chapter deals with sequences and collections (list, map, vector, set, and the Clojure “seq” abstraction), functions common to many types of sequences, and functions that are specific to specific collection types.

I was going to add some of the samples that I went through, but I cannot figure out how to get decent looking Clojure code in WordPress anymore. I do not like the Gutenberg editor at all, and it does now work as well with the classic editor as it used to. I found one called “Enlighter” which sounded good, but it does not do Clojure or any kind of Lisp. Given that WordPress really wants people to use the Gutenberg editor, at some point I might look for something other than WordPress.

The notes are on github (link here). I will update my page listing the functions in the Clojure API (some are in the Set API); I will just point to this page, and if you want to see the examples, then go to the page on Github.

Here are the functions discussed in the notes:

  • assoc
  • cons
  • dissoc
  • drop-while
  • even?
  • every?
  • file-seq (not in notes, in a Clojure file on github
  • filter
  • first
  • for
  • get
  • hash-set
  • interleave
  • line-seq
  • list
  • map
  • next
  • peek
  • pop
  • rest
  • select-keys
  • seq
  • set
  • some
  • split-at
  • split-with
  • subvec
  • take-while
  • vector
  • set/difference
  • set/intersection
  • set/join
  • set/project
  • set/select
  • set/union

Going through this book is taking a while (I admit I don’t do it every day). But I have tried learning languages from koans and puzzles, and I just wind up trying random functions from API and not being very productive.

You’re welcome.

Image from “Grec 74”, a 12th century manuscript housed at the Bibliothèque nationale de France. Source gallica.bnf.fr / BnF; image assumed allowed under Public Domain.

Every and Any in Clojure

Today we look at a few functions in Clojure that take predicates and look at collections: any?, every?, not-every? and not-any?

any? seems to return true for any argument. According to the docs, it is used for clojure.spec. Maybe when I learn more about spec, that will make sense. You would think a function called “any?” would be useful if you had a collection of numbers, and wanted to know if any of them are odd, or greater than 10, or something like that. Perhaps they could have given it a better name than “any?”

every?, not-any? and not-every? behave as you would expect them to. You can always simulate “intuituve-any?” by calling “not-any?” and then calling “not”.

user> (def evens [2 4 6 8])
#'user/evens                                                                                                                                                             
user> (def evens-with-9 [2 3 4 6 8 9])
#'user/evens-with-9
user> (every? even? evens)
true
user> (every? even? evens-with-9)
false
user> (not-every? even? evens)
false
user> (not-every? even? evens-with-9)
true
user> (not-every? odd? evens)
true
user> (not-every? odd? evens-with-9)
true
user> (not-any? odd? evens)
true
user> (not-any? odd? evens-with-9)
false
user> (any? 'g)
true
user> (any? nil)
true
user> (not (not-any? odd? evens))
false
user> (not (not-any? odd? evens-with-9))
true

You’re welcome.

Image from the Rheinau Psalter, a 13th century manuscript housed at Central Library of Zurich. Image from e-Codices. This image is assumed to be allowed under Fair Use.

Reduce In Clojure

The last of the Big Three Higher Order Functions in Clojure is reduce. In other languages it is called fold, accumulate, aggregate, compress or inject.

Sometimes reduce is compared to aggregate functions in databases, like average, count, sum, min or max. These functions take multiple values, and produce one value. Some functions in Clojure, like the four main math functions (+, -, * and /) are processed through reduce if they are called with more than two values.

I would like to point out that unlike the other aggregation functions that databases have, “average” cannot be done with reduce. To do an average, you sum the amounts, and then divide by the number of elements. No implementation of reduce (or fold or whatever you call it) keeps track of the number of elements and allows for an additional operation at the end. Maybe some languages or implementations have a reduce and an enhanced-reduce to cover this case.

The first parameter to Clojure’s reduce is a function that takes two parameters. There is an optional parameter that is an initial value, and the last argument is the collection you want to process. Reduce takes either the option initial value and the first element of the collection, or the first two elements of the collection, and passes them to the function. The output of the function is then used along with the next element in the collection. Reduce keeps using the output from the previous element and the next element as arguments to the function until the collection is exhausted.

I cannot find it now, but I remember reading somewhere that reduce can be hard to understand because “reduce” is frankly not a good name for it, and there are many functions that do what reduce does. As stated, some of the functions in Clojure (like max, min, and some math functions) call reduce. So you are actually using reduce a lot under the covers.

There is a cartoon about this. A teacher gives a student a few chances to say what is “2 + 3” equal to. The student says it equal to “2 + 3” and it is also equal to “3 + 2”. The student gets an F. The cartoon ends with: “Sad fact: many math teachers do not know the difference between equality and reduction.” I admit I do not either. A lot of the cartoons are about Haskell, which I have no desire to learn.

;; call + on a bunch of numbers
user> (+ 1 2 3 4 5 6)
21
;; winning with "the duce"
user> (reduce + [1 2 3 4 5 6])
21
;; using 0 for initial value
user> (reduce + 0 [1 2 3 4 5 6])
21
;; use 0 for +, 1 for *, or you will get incorrect results
user> (reduce + 1 [1 2 3 4 5 6])
22
;; define a function to explain reduce w/print statements
user> (defn add-with-print [x y]          
  (println "x is " x ", y is " y  ", (+ x y) is " (+ x y))
  (+ x y))
#'user/add-with-print
;; use w/just collection
user> (reduce add-with-print [1 2 3 4 5 6])
x is  1 , y is  2 , (+ x y) is  3           
x is  3 , y is  3 , (+ x y) is  6           
x is  6 , y is  4 , (+ x y) is  10          
x is  10 , y is  5 , (+ x y) is  15         
x is  15 , y is  6 , (+ x y) is  21         
21
;; use w/0 as initial value
user> (reduce add-with-print 0 [1 2 3 4 5 6])
x is  0 , y is  1 , (+ x y) is  1           
x is  1 , y is  2 , (+ x y) is  3           
x is  3 , y is  3 , (+ x y) is  6           
x is  6 , y is  4 , (+ x y) is  10          
x is  10 , y is  5 , (+ x y) is  15         
x is  15 , y is  6 , (+ x y) is  21         
21
;; use with 1 as initial value
user> (reduce add-with-print 1 [1 2 3 4 5 6])
x is  1 , y is  1 , (+ x y) is  2           
x is  2 , y is  2 , (+ x y) is  4           
x is  4 , y is  3 , (+ x y) is  7           
x is  7 , y is  4 , (+ x y) is  11          
x is  11 , y is  5 , (+ x y) is  16         
x is  16 , y is  6 , (+ x y) is  22         
22
user>

You’re welcome.

Update 2022-02-10_22:33:14: I just read a post by Rich Hickey about transducers in which he talks a bit about reduce. The function you send to reduce takes two arguments: the result-so-far and the next item in the collection, and returns the next result-so-far.

Image from  Aurora Consurgens, a 15th century manuscript housed at Central Library of Zurich. Image from e-Codices. This image is assumed to be allowed under Fair Use.

The Map Function In Clojure

The next member of the Big Three Higher Order Functions is “map“.

I don’t think “map” is a good name for it because 1. There is a data structure named “map” (which has different names in different languages) and 2. I don’t think it is the most descriptive name for it. “apply-to-every” or “apply-to-all” might be better.

The “map” function takes a function and one or more collections, and applies the function to each member in the collection. In Clojure, if you send multiple collections, they will be concatenated into one output collection.

I think the reason it is called “map” is that you are using a function to map a collection onto another collection.

Clojure also has “mapv” which does the same thing as “map” except that it returns a vector, and “mapcat” which takes functions that take collections as arguments. The functions sent to map and mapv take single arguments (if you send one collection) or multiple args, but not collections.

; a function that prints out what it's doing to better understand "map"
user> (defn inc-p [x]
  (println "x is " x " (inc x) is " (inc x))
  (inc x))

#'user/inc-p
user> (map inc-p [1 2 3 4])
x is  1  (inc x) is  2
x is  2  (inc x) is  3
x is  3  (inc x) is  4
x is  4  (inc x) is  5
(2 3 4 5)
user> (mapv inc-p [1 2 3 4])
x is  1  (inc x) is  2
x is  2  (inc x) is  3
x is  3  (inc x) is  4
x is  4  (inc x) is  5
[2 3 4 5]

;; if you send multiple collections, your function has to be able to take multiple args
user> (map inc-p [1 2 3 4] [2 3 4 5])
Error printing return value (ArityException) at clojure.lang.AFn/throwArity (AFn.java:429).
Wrong number of args (2) passed to: user/inc-p
user> (map + [1 2 3 4] [2 3 4 5])
(3 5 7 9)
user> (mapc + [1 2 3 4] [2 3 4 5])
Syntax error compiling at (on-demand-repl:localhost:42447(clj)*:112:7).
Unable to resolve symbol: mapc in this context
user> (mapv + [1 2 3 4] [2 3 4 5])
[3 5 7 9]
user> (mapv + [1 2 3 4] [2 3 4 ])
[3 5 7]
user>


user> (mapcat rest [1 2 3 4])
Execution error (IllegalArgumentException) at user/eval5576 (form-init2164788925227159491.clj:74).
Don't know how to create ISeq from: java.lang.Long
user> (mapcat rest [[1 2 3 4]])
(2 3 4)
user> (mapcat rest [[1 2 3 4] [2 3 4 5]])
(2 3 4 3 4 5)
user> 

You’re welcome.

Maps In Clojure

I was planning on looking into Clojure’s map function, the second of the Big Three Higher-Order Functions. But looking over my Clojure API page, I noticed there were a few functions with the word “map” in it that I had not gone over yet that deal with the map data structure.

Just as “map” is a noun and a verb in Clojure, the map data structure goes by different names in different programming languages: associative array, map, dictionary, symbol table, hash, hash table.

Anyway, I have looked at some of these functions earlier, but here are some more functions dealing with maps:

user> (sorted-map :a "value a" :b "value b" :c 33)
{:a "value a", :b "value b", :c 33}
user> (sorted-map :a "value a" :c "value c" :b 33)
{:a "value a", :b 33, :c "value c"}
user> (hash-map :a "value a" :c "value c" :b 33)
{:c "value c", :b 33, :a "value a"}
user> (array-map :a "value a" :c "value c" :b 33)
{:a "value a", :c "value c", :b 33}
user> (array-map :a "value a" :c "value c" :b 33 :c "more c")
{:a "value a", :c "more c", :b 33}
; honestly, I am not clear what he order is
user> (reverse (array-map :a "value a" :c "value c" :b 33 :c "more c"))
([:b 33] [:c "more c"] [:a "value a"])
user> (first (array-map :a "value a" :c "value c" :b 33 :c "more c"))
[:a "value a"]
user> (first (reverse (array-map :a "value a" :c "value c" :b 33 :c "more c")))
[:b 33]

user> (hash-map :a "value a" :d "what is d?" :c "value c" :b 33)
{:c "value c", :b 33, :d "what is d?", :a "value a"}
user> (hash-map :a "value a" :c "value c" :b 33 :b "again")
{:c "value c", :b "again", :a "value a"}
user> (hash-map :a "value a" :c "value c" :b 33 :b "again" :b "Third b")
{:c "value c", :b "Third b", :a "value a"}
user> (sorted-map-by > :a "value a" :c "value c" :b 33)
Execution error (ClassCastException) at user/eval5630 (form-init12885185727845486110.clj:148).                                                                           
class clojure.lang.Keyword cannot be cast to class java.lang.Number (clojure.lang.Keyword is in unnamed module of loader 'app'; java.lang.Number is in module java.base of loader 'bootstrap')   
user> (sorted-map-by compare :a "value a" :c "value c" :b 33)
{:a "value a", :b 33, :c "value c"}
user> (sorted-map-by compare :a "value a" :c "value c" :b 33 :c "more c")
{:a "value a", :b 33, :c "more c"}

user> (reverse (sorted-map-by compare :a "value a" :c "value c" :b 33))
([:c "value c"] [:b 33] [:a "value a"])
user> (sorted-map-by < 2 "value a" 33 "value c" 4 "v4")
{2 "value a", 4 "v4", 33 "value c"}
user> (sorted-map-by > 2 "value a" 33 "value c" 4 "v4")
{33 "value c", 4 "v4", 2 "value a"}
user> (sorted-map-by > 2 "value a" 33 "value c" 4 "v4" 33 "more 33")
{33 "more 33", 4 "v4", 2 "value a"}


user> (def empty-hm (hash-map))
#'user/empty-hm
user> (def empty-am (array-map))
#'user/empty-am
user> (def empty-sm (sorted-map))
#'user/empty-sm
user> 
; zipmap will create a map from a collection of keys and a collection of values
user> (zipmap [:a :b :c] [1 2 3])
{:a 1, :b 2, :c 3}
user> (zipmap  '(:a :b :c) '(1 2 3))
{:a 1, :b 2, :c 3}
; does not work with sets
user> (zipmap  #{:a :b :c} #{1 2 3})
{:c 1, :b 3, :a 2}
user> 
user> (map? (zipmap  '(:a :b :c) '(1 2 3)))
true
user> (map? (zipmap  #{:a :b :c} #{1 2 3}))
true
user> (map? #{1 2 3})
false
user>

You’re welcome.

Clojure Filter

The first higher-order function in Clojure I will look at is filter. It takes a predicate and a collection. It returns all the members of the collection for which the predicate is true. The docs say that it takes a “predicate”, but that predicate duty is frequently fulfilled by a function.

I tried calling filter with “true” as the predicate, and it did not work. I guess in Clojure (and probably all Lisps), a predicate is just a function that returns a boolean.

Filter is one of the Big Three Higher-Order Functions, the other two being Map and Reduce. This one actually has a name that describes what it does.

Clojure has two versions of filter: filter and filterv. The difference is that filter returns a lazy sequence, and filterv returns a vector.

user> (filter even? [1 2 3 4])
(2 4)
user> (filterv even? [1 2 3 4])
[2 4]

If filter is called with just the predicate, it will return a transducer. I will cover transducers later.

There are also another “filter” function in the clojure.core.reducers namespace. It takes the same arguments as the core filter, but when I tried it in a REPL I got an error. After looking at the Clojure page on reducers, I think the functions in that namespace only work if you chain them together.

You’re welcome.

A Look at cond Function

Here is a look at the “cond” function in Clojure. It’s like “if”, but with more conditions.

 

user> (defn test-num-same-line [x]
  (cond
    (< x 10) "less than"
    (> x 20) "greater than"))
#'user/test-num-same-line
user> (test-num-same-line 9)
"less than"
user> (test-num-same-line 29)
"greater than"
user> (test-num-same-line 19)
nil
;; the conditions and expressions can also be on different lines
user> (defn test-num-diff-line [x]
  (cond
    (< x 10)
    "less than"
    (> x 20)
    "greater than"))
#'user/test-num-diff-line
user> (test-num-diff-line 9)
"less than"
user> (test-num-diff-line 29)
"greater than"
user> (test-num-diff-line 19)
nil
;; let us come up with an else or "catch-all" condition
user> (defn test-num-with-else [x]
  (cond
    (< x 10) "less than"
    (> x 20) "greater than"
    :else "Between 10 and 20"))
#'user/test-num-with-else
user> (test-num-with-else 9)
"less than"
user> (test-num-with-else 29)
"greater than"
user> (test-num-with-else 19)
"Between 10 and 20"
;; you can use any keyword for the last condition; it does not have to be "else"
user> (defn test-num-with-other-keyword [x]
  (cond
    (< x 10) "less than"
    (> x 20) "greater than"
    :north "Between 10 and 20"))
#'user/test-num-with-other-keyword
#'user/test-num-with-other-keyword
user> (test-num-with-other-keyword 9)
"less than"
user> (test-num-with-other-keyword 29)
"greater than"
user> (test-num-with-other-keyword 19)
"Between 10 and 20"

You’re welcome.

loop, recur and comp

Here is some code for a few functions that are mentioned in Chapter Five of Clojure For The Brave And True, along with a few modifications/experiments that I did.

 

;; sum with recur, based on Brave And True chapter 5
user> (defn sum
  ([vals]
   (sum vals 0))
  ([vals accumulating-total]
   (println "sum w/recur and vals: ", vals, " and accumulating-total: ", accumulating-total)
   (if (empty? vals)
     accumulating-total
     (recur (rest vals) (+ (first vals) accumulating-total)))))
#'user/sum
user> (sum [3 4 5])
sum w/recur and vals:  [3 4 5]  and accumulating-total:  0                                                                                                                        
sum w/recur and vals:  (4 5)  and accumulating-total:  3                                                                                                                          
sum w/recur and vals:  (5)  and accumulating-total:  7                                                                                                                            
sum w/recur and vals:  ()  and accumulating-total:  12                                                                                                                            
12

;; sum with loop/recur
user> (defn sum-loop
  ([vals]
   (sum-loop vals 0))
  ([vals accumulating-total]
   (println "sum-loop w/recur and vals: ", vals, " and accumulating-total: ", accumulating-total)
   (loop [loop-vals  vals
          loop-accum accumulating-total]
     (println "In loop with loop-vals: ", loop-vals, " and loop-accum: ", loop-accum)
       (if (empty? loop-vals)
         loop-accum
         (recur (rest loop-vals) (+ (first loop-vals) loop-accum))))))
#'user/sum-loop
user> (sum-loop [3 4 5])
sum-loop w/recur and vals:  [3 4 5]  and accumulating-total:  0                                                                                                                   
In loop with loop-vals:  [3 4 5]  and loop-accum:  0                                                                                                                              
In loop with loop-vals:  (4 5)  and loop-accum:  3                                                                                                                                
In loop with loop-vals:  (5)  and loop-accum:  7                                                                                                                                  
In loop with loop-vals:  ()  and loop-accum:  12                                                                                                                                  
12
user> 
;; so it looks like without "loop", "recur" will call the function each time
;; so loop/recur is a better use of memory

;; comp can combine functions to make new functions
user> ((comp inc *) 2 3)
7
;; the right-most function can accept multiple args
;; but rest in the chain must accept only one
user> ((comp * inc ) 2 3)
ArityException Wrong number of args (2) passed to: core/inc  clojure.lang.AFn.throwArity (AFn.java:429)                                                                           

;; you can combine more than two
user> ((comp integer? inc *) 2 3)
true

;; use of comp from Brave And True to view a map:
user> (def character
  {:name "Smooches McCutes"
   :attributes {:intelligence 10
                :strength 4
                :dexterity 5}})
(def c-int (comp :intelligence :attributes))
#'user/character
#'user/c-int
user> (c-int character)
10
;; "get-in" can do the same thing with a nested map, 
;; but is a bit less elegant in usage
user> (get-in character [:attributes :intelligence])
10

You’re welcome.

(not) and its (complement)

Here is a quick look at the Clojure functions (not) and (complement):

user=> (not (= 1 2))
true
user=> (not (= 1 1))
false
user=> (not (> 2 3))
true
user=> (not (< 2 3))
false
;; from the cheat sheet
user=> (def not-empty? (complement empty?))
#'user/not-empty?
user=> (not-empty? []) 
false
;; now try with "not" directly
user=> (not (empty? []))
false
user=> (not-empty? [1 2])
true
user=> (not (empty? [1 2]))
true

;; from the cheat sheet
user=> (defn contains-char? [the-string, the-char]
  #_=>          (some #(= the-char %) the-string))
#'user/contains-char?
user=> (contains-char? "abc" \b)
true
user=> (contains-char? "abc" \j)
nil
;; just for fun
user=> (not (some #(= \b %1) "abc"))
false
;; from the cheat sheet
user=> (def does-not-contain-char? (complement contains-char?))
#'user/does-not-contain-char?
user=> (does-not-contain-char? "abc" \b)
false
;; with "not"
user=> (not (contains-char? "abc" \b))
false
user=> (does-not-contain-char? "abc" \j)
true
user=> (not (contains-char? "abc" \j))
true

;; so "not" and "complement" kind of do the same thing
;; from the cheat sheet
user=> (map (complement even?) '(1 2 3 4))
(true false true false)
user=> (map even? '(1 2 3 4))
(false true false true)
;; but "not" will throw an error
user=> (map (not even?) '(1 2 3 4))
ClassCastException java.base/java.lang.Boolean cannot be cast to clojure.lang.IFn  clojure.core/map/fn--4785 (core.clj:2646)
user=> (map (not #(even? %1)) '(1 2 3 4))
ClassCastException java.base/java.lang.Boolean cannot be cast to clojure.lang.IFn  clojure.core/map/fn--4785 (core.clj:2646)
user=> (map (not even? %1) '(1 2 3 4))
CompilerException java.lang.RuntimeException: Unable to resolve symbol: %1 in this context, compiling:(null:1:6) 
user=> 
;; so "not" will not work in a higher-order function
;; but everywhere else, they are kind of the same thing

 

 

You’re welcome.

More Collection Functions

user=> (max 0 1 2)
2
user=> (max 0 1 2 "a")

ClassCastException java.base/java.lang.String cannot be cast to java.base/java.lang.Number  clojure.lang.Numbers.gt (Numbers.java:229)
user=> (max [0 1 2])
[0 1 2]
user=> (max [0 1 2] "a")

ClassCastException java.base/java.lang.String cannot be cast to java.base/java.lang.Number  clojure.lang.Numbers.gt (Numbers.java:229)
user=> (apply max [0 1 2])
2
user=> (vector (concat [1 2 3 4] [5]))
[(1 2 3 4 5)]
user=> (vec (concat [1 2 3 4] [5]))
[1 2 3 4 5]
user=> (concat [1 2] [3 4])
(1 2 3 4)
user=> (concat 1 2 3)

IllegalArgumentException Don't know how to create ISeq from: java.lang.Long  clojure.lang.RT.seqFrom (RT.java:542)
user=> (into (hash-map) [ {:a 1} {:c 3} {:b 2} ] )
{:a 1, :c 3, :b 2}
user=> (into (hash-map) [ :a 1 :c 3 :b 2 ] )

IllegalArgumentException Don't know how to create ISeq from: clojure.lang.Keyword  clojure.lang.RT.seqFrom (RT.java:542)
user=> (into (hash-map) [ [:a 1] [:c 3] [:b 2] ] )
{:a 1, :c 3, :b 2}
user=> (into (hash-map) [:a 1] [:c 3] [:b 2]  )

ArityException Wrong number of args (4) passed to: core/into  clojure.lang.AFn.throwArity (AFn.java:429)
user=> (into (hash-map) '( [:a 1] [:c 3] [:b 2] ) )
{:a 1, :c 3, :b 2}
user=> (into {} '( [:a 1] [:c 3] [:b 2] ) )
{:a 1, :c 3, :b 2}
user=> (into [1 2 3] 4)

IllegalArgumentException Don't know how to create ISeq from: java.lang.Long  clojure.lang.RT.seqFrom (RT.java:542)
;; "into" can be used to add to a collection, but not the best way
user=> (into [1 2 3] [4])
[1 2 3 4]
user=> (into [1 2 3] (vector 4))
[1 2 3 4]
user=> (into #{1 2 3} (hash-set 4 5))
#{1 4 3 2 5}


user=> (conj [1 2 3 4] 0)
[1 2 3 4 0]
user=> (conj '(1 2 3) 4)
(4 1 2 3)
user=> (concat [1 2 3 4] [5])
(1 2 3 4 5)
user=> (conj [1 2 3 4] 5)
[1 2 3 4 5]
user=> (vector (concat [1 2 3 4] [5]))
[(1 2 3 4 5)]
user=> (vec (concat [1 2 3 4] [5]))
[1 2 3 4 5]
user=> (vector 1 2 3)
[1 2 3]
user=> (vec 1 2 3)

user=> (cons 0 [1 2 3 4]) ;; puts 0 at front, returns list
ArityException Wrong number of args (3) passed to: core/vec  clojure.lang.AFn.throwArity (AFn.java:429)
(0 1 2 3 4)
user=> (vec (cons 0 [1 2 3 4])) ;; puts 0 at front, converts to vector
[0 1 2 3 4]
user=> (conj  [1 2 3 4] 0) ;; puts 0 at the end
[1 2 3 4 0]
user=> (cons 0 '(1 2 3 4))
(0 1 2 3 4)
user=> (conj '(1 2 3 4) 0)
(0 1 2 3 4)
user=> 
;; cons always returns list
;; conj returns the same type
user=> (cons 0 [1 2 3 4])
(0 1 2 3 4)
user=> (cons 0 '(1 2 3 4))
(0 1 2 3 4)
user=> (conj [1 2 3 4] 0)
[1 2 3 4 0]
user=> (conj '(1 2 3 4) 0)
(0 1 2 3 4)
user=> (cons 0 #{1 2 3})
(0 1 3 2)
user=> (conj #{1 2 3} 4 5)
#{1 4 3 2 5}
;; and conj can take multiple elements to add
user=> (conj [1 2 3 4] 0 5 6)
[1 2 3 4 0 5 6]
;; so cons for list, conj for vectors
;; cons, arg is in front and added to front
;; conj, arg is in back, and added to back

I am going through Clojure For The Brave and True, and I have included some of the functions that I encounter while reading the book.

 

You’re welcome.