(inc 1)
;=> 2
If the syntax is good enough for the information, it should be good enough for the meta-information.
Java | Clojure |
---|---|
int i = 5; | (def i 5) |
if (x == 1) return y; else return z; | (if (= x 1) y z) |
x * y * z; | (* x y z) |
foo(x, y, z); | (foo x y z) |
foo.bar(x); | (.bar foo x) |
Evaluated as function calls
(inc 1)
;=> 2
(+ 1 2)
;=> 3
Always in prefix form
()
;=> ()
(cons 1 ())
;=> (1)
(cons 1 (cons 2 ()))
;=> (1 2)
(list 1 2 3)
;=> (1 2 3)
[1 2 3 4]
Preferred over lists; easier to write
Order 1 count and lookup by index
(get [10 20 30 40 50] 3)
;=> 40
Similar to arrays, but can be added to
(conj [1 2 3] 4)
;=> [1 2 3 4]
(pop [1 2 3 4])
;=> [1 2 3]
(assoc [1 2 3 4] 0 5)
;=> [5 2 3 4]
(vec (list 1 2 3 4))
;=> [1 2 3 4]
(cons 0 [1 2 3 4])
;=> (0 1 2 3 4)
(def a [1 2 3 4])
(= a a)
;=> true
(def b [1 2 3 4])
(= a b)
;=> true
a and b are different objects |
(identical? a b)
;=> false
(= [1 2 3 4] (list 1 2 3 4))
;=> true
Different types, but considered equal
Usually lowercase-words-hyphenated
Begin with an alphabet character
Can contain numbers and punctuation
Identifiers
inc
foo
(quote foo)
'foo
quote
means don’t resolve or evaluate
(quote (1 2))
;=> (1 2)
'(1 2)
;=> (1 2)
Without quote we have a problem:
(1 2)
;=> Exception: Long cannot be cast to IFn
Tried to apply 1 as a function |
Type | Value |
---|---|
Long | 1 |
Double | 3.14 |
BigInteger | 1000000000000N |
BigDecimal | 1000000000000.1M |
Exponents | 1e3 |
Ratio | 2/5 |
"This is a string."
Characters written with a backslash
\a \b \c \newline \tab \space
{"name" "Fate of the Furious" "sequence-number" 8 "rotten-tomatoes" 0.66 "imdb" 0.67}
Order 1 lookup, "add", "delete" by key
Tuned to be fast
Replacement for structs/objects
Versatile; used often in Clojure code
:my-keyword
Shorthand identifiers
Begin with a colon
Often used as keys in hashmaps
{:name "Fate of the Furious" :sequence-number 8 :rotten-tomatoes 0.66 :imdb 0.67}
(get {:a 1} :a)
;=> 1
(get {:a 1} :b 2)
;=> 2
(assoc {:a 1} :b 2)
;=> {:a 1, :b 2}
(dissoc {:a 1} :a)
;=> {}
(update {:a 2} :a inc)
;=> {:a 3}
(update {:a [1 2 3]} :a conj 4)
;=> {:a [1 2 3 4]}
(merge {:a 1} {:b 2})
;=> {:a 1, :b 2}
Commas are optional and treated as whitespace
(= {:a 1, :b 2, :c 3} {:a 1 :b 2 :c 3})
Prefer newlines
{:a 1 :b 2 :c 3}
{:name "Fate of the Furious" :sequence-number 8 :ratings {:rotten-tomatoes 0.66 :imdb 0.67}}
Ratings are a nested map
{[1 2] {:name "diamond", :type :treasure} [3 4] {:name "dragon", :type :monster}}
A map with vector coordinate keys, and map values
(assoc-in {:a {:b {:c 1}}} [:a :b :d] 2)
;=> {:a {:b {:c 1, :d 2}}}
(update-in {:a {:b {:c 1}}} [:a :b :c] inc)
;=> {:a {:b {:c 2}}}
(get-in {:a {:b {:c 1}}} [:a :b :c])
#{1 2 3}
Near constant time lookup
(contains? #{1 2 3} 3)
;=> true
(conj #{1 2 3} 4)
;=> #{1 2 3 4}
(disj #{1 2 3} 2)
;=> #{1 3}
union
, difference
and intersection
are available in the clojure.set
namespace
src/training/my_namespace.clj
(ns training.my-namespace (:require [clojure.set :as set]) (:import (java.time Instant Duration))) (set/union #{1 2 3} #{3 4}) => #{1 2 3 4}
The name must match path and filename
my-namespace
→ my_namespace.clj
training.
→ training/
(require '[clojure.set :as set]) (import (use 'clojure.set) (require '[clojure.set :refer :all])
Avoid use and :refer :all |
ns works in the REPL! |
(ns my.namespace (:require [clojure.set :as set]))
Expressions which are evaluated to results
If an expression needs to be compiled, it will be
Can be loaded from files or evaluated dynamically
Unit of compilation is a form
Nominate an entry point namespace/function
:my.namespace/rect
Shortcut:
::rectangle
;=> :my.namespace/rectangle
::
expands to the current namespace
(defn square [x]
(* x x))
(square 2)
;=> 4
(defn square
"Multiplies a number by itself"
[x]
(* x x))
(square 2)
;=> 4
(def x 1)
;=> #'my.namespace/x
x
;=> 1
Global mutable reference
Use sparingly
#'
means var
(var x)
;=> #'x
#'x
;=> #'x
The symbol x
resolves to a Var
Vars are automatically dereferenced when evaluated
Dereferrencing returns the value associated with the Var
Avoid using vars like variables
defn
is actually def
with a function value
Can use #'x
or (var x)
to access the Var
(let [x 1]
(inc x))
;=> 2
Bind symbols to values in a scope
Shadow existing bindings
Prefer let
over def
(let [[x y] [1 2]]
(+ x y))
;=> 3
Literal data structure containing symbols
Matches structure
(defn normalize1 [v] (let [x (first v) y (second v) length (Math/sqrt (+ (* x x) (* y y)))] [(/ x length) (/ y length)]))
Avoid extracting substructure manually:
(defn normalize2 [[x y]] (let [length (Math/sqrt (+ (* x x) (* y y)))] [(/ x length) (/ y length)]))
(let [[a b] (list 1 2)]
b)
;=> 2
(seq "abc")
;=> (\a \b \c)
(seq {:a 1, :b 2, :c 3})
;=> ([:a 1] [:b 2] [:c 3])
(seq? 8)
;=> false
(let [[a b] "abc"]
b)
;=> \b
(drop 2 [0 0 0 0])
;=> (0 0)
(range 5)
;=> (0 1 2 3 4)
(take 2 "abcd")
;=> (\a \b)
Many sequence oriented functions
Never modify the original sequence
Often lazy
Lazy means that the next value in the sequence is only calculated when it is made use of
Stream abstraction; only the currently used item needs to be in memory
Useful for processing files that don’t fit in memory
(for [i (range 10)]
(* i i))
;=> (0 1 4 9 16 25 36 49 64 81)
(for [file ["a" "b" "c"]
rank [1 2]]
(str file rank))
;=> ("a1" "a2" "b1" "b2" "c1" "c2")
(for [i (range 10)
:when (odd? i)
:let [square (* i i)]]
square)
;=> (1 9 25 49 81)
(let [m {:a 1, :b 2, :c 3}]
(for [[k v] m]
[v k]))
;=> ([1 :a] [2 :b] [3 :c])
Destructuring is available in any binding form |
&
(defn sub [& vs]
vs)
(sub 1 2 3 4)
;=> (1 2 3 4)
Variadic means variable number of arguments
Arity means number of arguments
We could have just passed a vector instead
Calls a function with a sequence of arguments
(apply + [1 2 3 4])
;=> 10
Most mathematical functions are variadic:
(+ 1 2 3)
;=> 6
(def x {:a 10
:b 20})
(let [{a :a, b :b} x]
(+ a b))
;=> 30
(let [{:keys [a b]} x]
(+ a b))
;=> 30
(def y {"a" 10
"b" 20})
(let [{a "a", b "b"} y]
(+ a b))
;=> 30
(let [{:strs [a b]} y]
(+ a b))
;=> 30
(def x (range 5))
(first x)
;=> 0
(rest x)
;=> (1 2 3 4)
(let [[a & more] (range 5)]
a)
;=> 0
(let [[a & more] (range 5)]
more)
;=> (1 2 3 4)
(def movie {:name "Fate of the Furious"
:sequence-number 8
:ratings {:rotten-tomatoes 0.66
:imdb 0.67}})
(get-in movie [:ratings :imdb])
;=> 0.67
(let [{{:keys [imdb]} :ratings} movie]
imdb)
;=> 0.67
(defn f [{:keys [a b] :as x}]
x)
(f {})
;=> {}
(defn f [{:keys [a b] :or {a "default"}}]
a)
(f {})
;=> "default"
(defn f [x]
(let [defaults {:a "default"}
{:keys [a b]} (merge defaults x)]
a))
(f {})
;=> "default"
Removal of next form #_
#_(this form is removed) #_#_ (ignored-1) (ignored-2)
Temporarily remove a form when debugging code
Looks like a bug eyes emoji
#"pattern"
(re-seq #"\w+" "the quick brown fox")
;=> ("the" "quick" "brown" "fox")
See manual end of section 2
Set up the new namespace called training.syntax
(ns training.syntax)
Define a var called message
bound to the string "greetings"
(def message "greetings")
;=> nil
Print out the value of the var message
(prn message)
;;; "greetings"
;=> nil
prn keeps the quotes around strings; println does not |
Create a let
binding that binds the symbol message
to "well hello there"
, and prints out message
inside the let
block:
(let [message "well hello there"]
(prn message))
;;; "well hello there"
;=> nil
Print out message again, outside of the let
block:
(prn message)
;;; "greetings"
;=> nil
message global var is still the original value |
Create a let binding that destructures a map and prints the greeting and tone:
(def m {:greeting "good morning", :tone "happy"})
(let [{:keys [greeting tone]} m]
(prn greeting tone))
;=> "good morning" "happy"
Destructure a single map input and return a string combining greeting and tone:
(defn hi [{:keys [greeting tone]}]
(str greeting " - " tone))
(hi m)
;=> "good morning - happy"
Comments
Anything following a semicolon is a comment
Less common is the comment form: