Today I am starting Clojure for the Brave and True. My expectation is to skim chapter 1 & 2, and to read chapter 3. I will only take notes about the things that are new to me, or that I feel should be repeated for the sake of me learning them.
Since I am only writing little tidbits of information I will use bullet points:
- When using an
if
, the else expression is optional do
groups expressions together, useful if you need to do several things in the then statement of anif
when
is a like the combination ofif
anddo
, it's taking a binary expression and will evaluate all the other arguments if it is trueor
andand
are interesting in the sense that they will return the respectively the first and the last truthy value- In Clojure, you bind a name to a value with
def
. As opposed to JavaScript it is encourage to assign and re-assign (a lot less in TypeScript I found out). In Clojure you end up using functions and not modifying the symbols. There is usually not equivalent to reassigning in place in a datastructure.
- numbers
- string
- maps
{}
(similar to hashes or dictionaries in other languages)- they can be nested
- there are hash-maps and sorted-maps
hash-map
creates a map:(hash-map :a 1 :b 2)
- can also be used as a function to retrieve a value:
({:a 1} :a)
=>1
, but also(:a {:a 1})
=>1
- keywords
:a
- vectors
[1 2 3]
- you can also use
get
on them, with the index as the second argument
- you can also use
- lists
'(1 2 3)
- sets are collections of unique values
#{"hello" 1 :hi}
hash-set
creates a set- to get a value you can use
contains?
,get
, or use a keyword as function
- Functions that can take a function as an argument, or return a function are higher-order functions.
- It is possible to define multiple functions depending on how many arguments you pass to a function:
(defn multi-arity
;; 3-arity arguments and body
([first-arg second-arg third-arg]
(do-things first-arg second-arg third-arg))
;; 2-arity arguments and body
([first-arg second-arg]
(do-things first-arg second-arg))
;; 1-arity arguments and body
([first-arg]
(do-things first-arg)))
It's also a way to define default arguments:
(defn x-chop
"Describe the kind of chop you're inflicting on someone"
([name chop-type]
(str "I " chop-type " chop " name "! Take that!"))
([name]
(x-chop name "karate")))
You can bind names to values within a collection:
(defn destruct
[[a b & c]]
(println (str "1 " a))
(println (str "2 " b))
(println (str "rest " c)))
You can also destructure maps:
(defn destruct-map
[{a :a b :b}]
(println (str "1 " a))
(println (str "2 " b)))
=> (destruct-map {:a 1 :b 2})
An even shorter way is:
(defn announce-treasure-location
[{:keys [lat lng]}]
(println (str "Treasure lat: " lat))
(println (str "Treasure lng: " lng)))
Using :as
lets you retain access to the original map:
(defn receive-treasure-location
[{:keys [lat lng] :as treasure-location}]
(println (str "Treasure lat: " lat))
(println (str "Treasure lng: " lng))
;; One would assume that this would put in new coordinates for your ship
(steer-ship! treasure-location))
I have not covered loop
yet.
(loop [i 0] ; initialize i at 0
(printl (str i))
(if (> i 4) ; here will stop the loop
(println "end")
(recur (int i)))) ; here we increment i and start the loop again
Loop has much better performance than a recursive call.
(str "hello " "world")
(vector '(1 2 3))
(list 1 2 3)
(hash-map :a 1 :b 2 :c 3)
(hash-set 1 1 2)```
#### 2. Write a function that takes a number and adds 100 to it
```clojure
#(+ % 100)
3. Write a function, dec-maker, that works exactly like the function inc-maker except with subtraction:
(defn dec-maker
[n]
#(- % n))
(def dec9 (dec-maker 9))
(dec9 10)
(defn mapset
[f coll]
(set (map f coll)))
5. Create a function that’s similar to symmetrize-body-parts except that it has to work with weird space aliens with radial symmetry. Instead of two eyes, arms, legs, and so on, they have five.
(defn matching-parts
[part]
#{{:name (clojure.string/replace (:name part) #"^left-" "top-")
:size (:size part)}
{:name (clojure.string/replace (:name part) #"^left-" "right-")
:size (:size part)}
{:name (clojure.string/replace (:name part) #"^left-" "bottom-left-")
:size (:size part)}
{:name (clojure.string/replace (:name part) #"^left-" "bottom-right-")
:size (:size part)}})
(defn symmetrize-body-parts
"Expects a seq of maps that have a :name and :size"
[asym-body-parts]
(loop [remaining-asym-parts asym-body-parts
final-body-parts []]
(if (empty? remaining-asym-parts)
final-body-parts
(let [[part & remaining] remaining-asym-parts]
(recur remaining
(into final-body-parts
(conj (matching-parts part) part)))))))
Overall it was a good revision of what I have learned in Clojure from the Ground Up. The order of things is a little bit different which is refreshing. Some things we didn't cover in CFTGU were covered at the beginning of the chapter (like loops).
I also did 5 out of 6 of the exercises in this chapter. They were quite easy but that makes sense as it is the fist chapter a beginner would study. Let's end the day with a tally of what I completed:
- Chapter 3 of Clojure for the Brave and True
- 5 problems included in the first chapter