Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
(+ 1 2)
; KEYWORDS
; used to map values in hashes
(def person {:name "Andre"
:city "Florianopolis"})
; Keywords are functions that look themselves up in collections passed to them.
(:city person)
; Double-colon before a keyword creates it in the global namespace.
(def pizza {:name "Baggio"
:location "Florianopolis"
::location "-43 +12"})
pizza
(:user/location pizza)
(:location pizza)
;
; Keywords are named values. Means they have a name:
(name :user/location)
(namespace :user/location)
(namespace :location)
; SYMBOLS
; Are identifiers that evaluate to values (held by vars).
(defn average [numbers]
(/ (apply + numbers) (count numbers)))
(average [10, 20, 30])
; ^ average is a symbol. refers to the function held in var named average.
; Commas are optional
(= [1, 2, 3] [1 2 3])
; def defines variables in the current namespace.
(def x 1)
x
; *ns* shows the current namespace
*ns*
; ns function changes the current namespace
(ns user)
; CLOJURE SPECIAL FORMS
; 1. quote: suppresses evaluation of expressions. Symbols evaluate to themselves.
x
(quote x)
(symbol? (quote x))
; ' (aposthrofe) is the same as quote.
x
'x
; anything can be quoted.
; quoting lists supress evaluation, returning the list itself.
(+ x x)
'(+ x x)
(list? '(+ x x))
(list '+ 'x 'x)
; 2. do: evaluates a list of expressions. Last expression is the return value.
(do
(println "hi")
(apply * [4 5 6]))
; 3. def: define vars.
(def p "foo")
p
; 4. let: create local bindings.
(defn hypot
[x y]
(let [x2 (* x x)
y2 (* y y)]
(Math/sqrt (+ x2 y2))))
(hypot 3.0 4.0)
; all local bindings created by let are immutable
; 5. destructuring: pulls apart collections
; - sequential destructuring: works with sequential collections.
(def v [42 "foo" 99.2 [5 12]])
v
(let [[a b c] v]
(+ a c))
; destructuring can be nested.
(let [[a _ _ [b c]] v]
(+ a b c))
; & gets the rest.
(let [[a & rest] v]
rest)
; :as makes the next symbol hold the original values
(let [[x _ y :as original] v]
(conj original (+ x y)))
; - map destructuring: works with maps.
(def m {:a 5 :b 6
:c [7 8 9]
:d {:e 10 :f 11}
"foo" 88
42 false})
(let [{a :a b :b} m]
(+ a b))
(let [{f "foo"
v 42} m]
(+ f (if v 0 1)))
; map destructuring syntax can be used with vectors if indexes are provided.
(let [{a 1 b 4} [1 2 3 4 5]]
(+ a b))
; can be nested.
(let [{{a :e} :d} m]
(* 2 a))
; can be mixed with sequential destructuring
(let [{[x _ y] :c} m]
(+ x y))
; default values
(def m2 {:a 1 :b 2})
(let [{a :a b :x
:or {b 10}} m]
(+ a b))
; DRY, or mapping keys to locals with same name
(def chas {:name "Chas"
:age 31
:location "Massachusetts"})
(let [{:keys [name age location]} chas]
(format "%s is %s years old and lives in %s" name age location))
; Destructuring a vector of key-value pairs into a map: (using the rest - & - notation).
(def user-info [:username "andre" :name "Andre" :city "floripa"])
(let [[& {:keys [username name city]}] user-info]
(format "%s (%s) is from %s" username name city))
; 6. FN: create functions
(fn [x] (+ 10 x))
; calling functions with parentheses:
((fn [x] (+ 10 x)) 8)
; put a function in a var:
(def add-10 (fn [x] (+ 10 x)))
(add-10 8)
; defn is short for this (create and put it in a var)
(defn add-10 [x]
(+ 10 x))
(add-10 9)
; multi-arity functions:
(defn add-default
([x] (+ x 1))
([x y] (+ x y)))
(add-default 1)
(add-default 1 2)
; variadic functions: using destructuring in arguments
(defn concat-rest
[x & rest]
(apply str rest))
(concat-rest 1 2 3 4)
; zero-arity functions
(defn adder
[& attrs]
(apply + (or attrs [0])))
(adder)
(adder 1 2 3 5 7 9 11)
; keyword arguments, also using destructuring.
(defn now [] (java.util.Date.))
(defn make-user
[username & {:keys [email join-date]
:or {join-date (now)}}]
{:username username
:join-date join-date
:email email})
(make-user "andre"
:email "abernardes@gmail.com")
; literals for anonymous functions
(#(+ %1 %2) 2 1)
(read-string "#(+ %1 %2)")
; 7. IF conditional
(if true "andre" "bernardes")
; 8. Loop and Recur
(loop [x 5] ; implicit let form
(if (neg? x)
x ; final expression, returns its value
(recur (dec x)))) ; or back to the loop, rebinds x to (dec x)
; functions are a loop head:
(defn countdown
[x]
(if (zero? x)
:blastoff!
(do (println x)
(recur (dec x)))))
; ******************************************************************
; Functional programming in Clojure:
; - Preference for working with immutable values
; - Immutable data structures
; - Functions as values > higher order functions
; - Declarative processing of data
; - Incremental composition for higher level abstractions
; FIRST CLASS AND HIGHER ORDER FUNCTIONS
(defn call-twice [f x]
(f x)
(f x))
; Higher order functions: functions that receive other functions as
; arguments or return a function as a result. Examples:
; map
(map clojure.string/lower-case ["Andre" "Bernardes" "Oliveira"])
(map + [1 2 3] [4 5 6])
; reduce : reduce a collection to a single value
(reduce max [0 10 8 2])
(reduce + 1 [2 3 4])
(reduce
(fn [collection value]
(assoc collection value (* value value)))
{}
[1 2 3 4])
; Partial Apply
; apply: send an array as arguments to a function
(apply hash-map [:a 5 :b 6])
(apply + 1 2 [3 4 5])
; partial apply: define a function with preset arguments
(def only-strings (partial filter string?))
(only-strings [1 "a" 2 "b"])
; Function Composition
; comp: compose functions.
(defn negated-sum-str ; regular function
[& numbers]
(str (- (apply + numbers))))
(negated-sum-str 10 100 5 12)
(def negated-mult-str (comp str - *)) ;pipeline with comp
(negated-mult-str 1 2 5)
(defn split-camel [s]
(str/split s #"(?<=[a-z])(?=[A-Z])"))
(def camel->keyword
(comp keyword
str/join
(partial interpose \-)
(partial map str/lower-case)
#(str/split % #"(?<=[a-z])(?=[A-Z])")))
(camel->keyword "FooBar")
(camel->keyword "fooBar")
(def camel-pairs->map
(comp (partial apply hash-map)
(partial map-indexed (fn [i x]
(if (odd? i)
x
(camel->keyword x))))))
(camel-pairs->map ["FooBar" 3 "lowFooBar" 5])
; Writing Higher-order functions
; a function that returns a function
(defn adder
[n]
(fn [x] (+ n x)))
((adder 5) 18)
(defn doubler
[f]
(fn [& args]
(* 2 (apply f args))))
((doubler +) 1 2 3)
; example: logging system
; print-logger produces a function that logs messages to a writer
(defn print-logger
[writer]
#(binding [*out* writer]
(println %)))
; file-logger produces a function that uses print-logger to
; print to a file.
(require 'clojure.java.io)
(defn file-logger
[file]
#(with-open [f (clojure.java.io/writer file :append true)]
((print-logger f) %)))
; multi-logger returns a function that writes to multiple loggers
; at once
(defn multi-logger
[& logger-fns]
#(doseq [f logger-fns]
(f %)))
(def log (multi-logger
(print-logger *out*)
(file-logger "messages.log")))
; adds timestamps to the logger
(defn timestamped-logger
[logger]
#(logger (format "[%1$tY-%1$tm-%1$te %1$tH:%1$tM:%1$tS] %2$s" (java.util.Date.) %)))
(def log-timestamped
(timestamped-logger
(multi-logger
(print-logger *out*)
(file-logger "messages.log"))))
; PURE FUNCTIONS
; memoization
(defn prime?
[n]
(cond
(== 1 n) false
(== 2 n) true
(even? n) false
:else (->> (range 3 (inc (Math/sqrt n)) 2)
(filter #(zero? (rem n %)))
empty?)))
(time (prime? 1125899906842679))
(let [m-prime? (memoize prime?)] ; memoized function
(time (m-prime? 1125899906842679))
(time (m-prime? 1125899906842679))) ; second time returns cached result
; * Important: always use memoize in local scopes
; CHAPTER 3 - COLLECTIONS AND DATA STRUCTURES
; 1. Used in terms of abstractions
; 2. Immutable and Persistent
; COLLECTIONS
(conj [1 2] 3)
(seq #{1 2 3})
(count {:a 1 :b 2})
(empty '(1 2 3))
(= [1 2] [1 2])
; SEQUENCES
(seq "Clojure")
(seq {:a 5 :b 6})
(first "Clojure")
(rest "clojure")
(next "clojure")
(rest [1]) ; Returns empty
(next [1]) ; Returns nil
; seqs are only "realised" when needed
(let [s (range 1e6)]
(time (count s))) ; takes a long time bc has to realise the seq before counting
(let [s (apply list (range 1e6))]
(time (count s))) ; lists are realized and track their size
; Creating Seqs
; cons - prepend value to collection, returns a seq
(cons 0 (range 1 5))
; list* - sequential cons.
; don't do this:
(cons 1 (cons 2 (range 3 5)))
; do this instead:
(list* 1 2 (range 3 5))
; Lazy Evaluation
(defn random-ints
"Returns a lazy seq of random integers in the range [0, limit)."
[limit]
(lazy-seq
(cons (rand-int limit)
(random-ints limit))))
(take 10 (random-ints 50))
; Fibonnaci sequence using a lazy seq
; I actually made this myself!
(defn fib
([]
(lazy-seq
(list* 0 1 1 (fib 1 1))))
([x1 x2]
(let [sum (+ x1 x2)]
(lazy-seq
(cons sum (fib x2 sum))))))
(take 10 (fib))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment