Skip to content

Instantly share code, notes, and snippets.

@JHawk
Created August 24, 2012 17:26
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JHawk/3453165 to your computer and use it in GitHub Desktop.
Save JHawk/3453165 to your computer and use it in GitHub Desktop.
Programming Clojure Second Edition Notes
; MAIN
; (defn -main [s]
; (hello "josh"))
(require '[clojure.string :as str])
; Ch.1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; (printnl "hi")
; (defn hello [name] (str "hello, " name))
; (conj #{} "Josh")
; (def viistors (ref #{}))
; (dosync (alter visitors conj "Josh"))
; (deref visitors)
; ;; syntax sugar for deref
; @visitors
; REPL ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; getting results
; user=> (+ 10 1)
; 11
; user=> (str *1)
; "11"
; ERRORS
; user=> (asdf)
; java.lang.Exception: Unable to resolve symbol: asdf in this context (NO_SOURCE_FILE:1)
; user=> (pst)
; java.lang.Exception: Unable to resolve symbol: pst in this context (NO_SOURCE_FILE:2)
; user=> (str *e)
; "java.lang.Exception: Unable to resolve symbol: pst in this context (NO_SOURCE_FILE:2)"
; user=>
; LOAD FILES
; user=> (load-file "~/workspace/mori/src/mori/core.clj")
; REQUIRE
; (require 'examples.introduction)
; user=> (require 'examples.introduction)
; user=> (take 10 examples.introduction/fibs)
; user=> (use 'examples.introduction)
; user=> (take 10 fibs)
; user=> (use :reload 'examples.introduction)
; INFO
; user=> (doc str)
; -------------------------
; clojure.core/str
; ([] [x] [x & ys])
; With no args, returns the empty string. With one arg x, returns
; x.toString(). (str nil) returns the empty string. With more than
; one arg, returns the concatenation of the str values of the args.
; nil
; user=> (source identity)
; (defn identity
; "Returns its argument."
; {:added "1.0"}
; [x] x)
; nil
; grep for info
; user=> (find-doc "redu")
; lots of results
; REPL ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Ch.1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Ch.2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; JAVA interop '.'
(.toUpperCase "hello")
; "HELLO"
(apply str (interleave "Josh" "Awesome"))
(if () "We are in Clojure!" "We are in Common Lisp!")
; MAPS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def inventors {:Lisp "McCarthy" :Clojure "Hickey"})
(:Lisp inventors)
(get inventors :Lisp "default response - no idea?!")
; RECORDS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defrecord Book [title author])
(def book1 (->Book "ABC..." "Me"))
(:title book1)
; less fun syntax
(Book. "Stuff" "Thing")
; reader macro ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def foo 10)
'foo
; functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; variable args
(defn date
"takes two daters and n chaperones"
([] println "nobody to go")
([lonely] println (str lonely " is lonely!"))
([person-1 person-2 & chaperones]
(println person-1 "and" person-2
"went out with" (count chaperones) "chaperones." )))
; anon function macro
; (filter #(> (count %) 2) (re-split #"\W+" "A fine day it is"))
; let binding
(defn id-words [txt]
(let [id-able? #(> (count %) 2)]
(filter id-able? (str/split txt #"\W+"))))
; returning a func
(defn make-greeter2 [greeting-prefix]
#(str greeting-prefix ", " %))
; destructuring ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn greet-author-2 [{fname :first-name}]
(println "Hello," fname))
; (let [[x y] [1 2 3]]
; [x y])
; (x [[let y :as coords] [1 2 3 4 5 6]]
; (str "x: " x ", y: " y ", total dimensions " (count coords)))
; CALLING JAVA CLASSES ;;;;;;;;;;;;;;;;;;;;;;
(def rnd (new java.util.Random))
(. rnd nextInt)
(. rnd nextInt 10) ;; with arg
(. Math PI) ;; static with .
; flow control ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn is-small? [number]
(if (< number 100)
"yes"
(do
(println "Saw a big number" number)
"no" )))
; recur
(defn countdown [result x]
(if (zero? x)
result
(recur (conj result x) (dec x))))
(countdown [] 5)
; seq and for
(defn indexed [coll] (map vector (iterate inc 0) coll))
; comprehension
(defn index-filter [pred coll]
(when pred
(for [[idx elt] (indexed coll) :when (pred elt)] idx)))
; meta
(def serializable-stu (with-meta stu {:serializable true}))
(^serializable-stu) ;; gets the meta data
; REPL meta info
; (meta #'str)
; adding meta data - prefer tail position
(defn shout
([s] (.toUpperCase s))
{:tag String})
; Ch.2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Ch.3 Seqs ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(first '(1 2 3))
; 1
(rest '(1 2 3)) ; tail
; (2 3)
(cons 0 [1 2 3])
; (0 1 2 3)
; REPL
(class (rest [1 2 3]))
; maps as seq - not sorted - use sorted-map - same true of sets
(first {:fname "Alpha" :lname "Beta"})
; -> [:lname "Beta"]
; conj and into - note - for seq it adds to front in rev - list adds to back in order
(conj '(1 2 3) :a)
; -> (:a 1 2 3)
(into [1 2 3] [:a :b])
; -> [1 2 3 :a :b]
; more comprehensions
(for [w ["whatever" "you" "want"]]
(format "<p>%s</p>" word))
; also supports :while
(take 10 (for [n (whole-numbers) :when (even? n)] n))
; multiple colls to iterate ;
(for [file "ABCD" rank (range 1 5)] (format "%c%d" file rank))
; -> ("A1" "A2" "A3" "A4" "B1" "B2" "B3" "B4" "C1" "C2" "C3" "C4" "D1" "D2" "D3" "D4")
; forcing a seq
(def x (for [i (range 1 3)] (do (println i) i)))
(doall x) ; returns the list - for pure functions
(dorun x) ; returns nil - for side effects (can work over very large collections - doesn't keep them in memory)
; java collections are seq-able
(first (.getBytes "hello"))
; -> 104
; regexp
(re-seq #"\w+" "the whatever and whatever")
; -> ("the" "whatever" "and" "whatever")
;;; VECTORS ;;;;
; vector index
(get [1 2 3] 1)
; -> 2
(get [1 2 3] 5)
; -> nil
(assoc [0 1 2 3 4] 2 "hi")
; -> [0 1 "hi" 3 4]
(subvec [1 2 3 4 5] 1 3)
; -> [2 3]
; re-read the relational mapping stuff - it's neat - end of ch 3
; too lazy to write notes
; Ch.3 Seqs ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Ch.4 Functional P ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn bad-fib [n]
(cond
(= n 0) 0
(= n 1) 1
:else (+ (bad-fib (- n 1))
(bad-fib (- n 2)))))
(defn tail-fib [n]
(letfn [(fib
[current next n]
(if (zero? n)
current
(recur next (+ current next) (dec n))))]
(fib 0N 1N n)))
; lazy-seq is neat
(defn lazy-fib
([] (concat [0 1] (lazy-fib 0N 1N)))
([a b]
(let [n (+ a b)]
(lazy-seq
(cons n (lazy-fib b n))))))
(defn fib-anamorphism []
(map first (iterate (fn [[a b]] [b (+ a b)]) [0N 1N])))
; LOOP RECUR
(defn head-pairs [coll]
(loop [cnt 0 coll coll]
(if (empty? coll)
cnt
(recur (if (= :h (first coll) (second coll))
(inc cnt)
cnt)
(rest coll)))))
(defn lazy-pairs [coll]
(let [take-pair (fn [c] (when (next c) (take 2 c)))]
(lazy-seq
(when-let [pair (seq (take-pair coll))]
(cons pair (lazy-pairs (rest coll)))))))
(defn lazy-head-pairs [coll]
(count (filter (fn [pair] (every? #(= :h %) pair))
(lazy-pairs coll))))
; PRIVATE TO NS DEFN
(defn- private-lazy-pairs [coll]
(count (filter (fn [pair] (every? #(= :h %) pair))
(partition 2 1 coll))))
; composition and doc setting
(def ^{:doc "Count items matching filter"}
count-if (comp count filter))
(defn- count-runs
"Count runs of n where pred is true."
[n pred coll]
(count-if #(every? pred %) (partition n 1 coll)))
; partial application
(def ^{:doc "Count pairs of heads"}
count-head-runs (partial count-runs 2 #(= % :h)))
; mutual recursion - declare inits multiple bindings with no val in one line
; NOT TCO
(declare my-odd? my-even?)
(defn my-odd? [n]
(if (= n 0) false (my-even? (dec n))))
(defn my-even? [n]
(if (= n 0) true (my-odd? (dec n))))
; trampoline manages RECUR for you to prevent stack overflow
(declare my-odd2? my-even2?)
; these defns return functions that trampoline can call
(defn my-odd2? [n]
(if (= n 0) false #(my-even2? (dec n))))
(defn my-even2? [n]
(if (= n 0) true #(my-odd2? (dec n))))
(trampoline my-even2? 1000000)
; memoize
(declare m f)
(defn m [n]
(if (zero? n)
0
(- n (f (m (dec n))))))
(defn f [n]
(if (zero? n)
1
(- n (m (f (dec n))))))
(def m-mem (memoize m))
(def f-mem (memoize f))
; Ch.4 Functional P ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Ch.5 State ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ref (sync) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def current-dog (ref "Wilfred"))
(def current-cat (ref "Smokey"))
(deref current-dog)
; short deref
@current-dog
(dosync
(ref-set current-dog "Archie")
(ref-set current-cat "Zap"))
(defrecord Message [sender text])
(def messages (ref ()))
; alter restarts transaction if collision with another transaction
(defn add-message [msg]
(dosync (alter messages conj msg)))
(add-message (->Message "Zap" "food.."))
(add-message (->Message "Archie" "more..food.."))
; commute doesn't care about transaction order
(defn add-message-commute [msg]
(dosync (commute messages conj msg)))
(def valid-mess
(partial every? #(and (:sender %) (:text %))))
; ref transaction validation - throws ex
(def messages2 (ref () :validator valid-mess))
; atom (sync) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def current-pet (atom {:name "Zap" :type "Cat"}))
@current-pet ; deref is same
(reset! current-pet {:name "Wilfred" :type "Dog"})
; swap! updates with a function call on the current val
(swap! current-pet assoc :name "Archie")
; agent (async) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(def counter (agent 0))
; send it an update fn
(send counter inc)
@counter ; deref is same
; await - blocks the current thread until a val is returned
; await-for - takes a timeout and throws an error if timeout
(def valid-counter (agent 0 :validator number?))
(send valid-counter (fn [_] "Exception State"))
; @valid-counter ; causes Exception
(clear-agent-errors valid-counter) ; resets to previous unbroken state
; bindings ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn ^:dynamic slow-double [n]
(Thread/sleep 100)
(* n 2))
(defn calls-slow-double []
(map slow-double [1 2 1 2 3 4 1 2]))
(time (dorun (calls-slow-double)))
; bind slow-double to a memoized version
(defn memo-slow-double []
(time
(dorun
(binding [slow-double (memoize slow-double)]
(calls-slow-double)))))
; Ch.5 State ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Ch.6 Protocols & Datatypes ;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; p 155 without ns
(defprotocol Vault
"Description of the Vault Protocol"
(init-vault [vault] "Initialize the Vault")
(vault-output-stream [vault] "Creates the Output Stream")
(vault-input-stream [vault] "Creates the Input Stream"))
(defn vault-key [vault]
(let [password (.toCharArray (.password vault))]
(with-open [fis (FileInputStream. (.keystore vault))]
(-> (doto (KeyStore/getInstance "JCEKS")
(.load fis password))
(.getKey "vault-key" password)))))
(deftype CryptoVault [filename keystore password]
Vault
(init-vault [vault]
(let [password (.toCharArray (.password vault))
key (.generateKey (KeyGenerator/getInstance "AES"))
keystore (doto (KeyStore/getInstance "JCEKS")
(.load nil password)
(.setEntry "vault-key"
(KeyStore$SecretKeyEntry. key)
(KeyStore$PasswordProtection. password)))]
(with-open [fos (FileOutputStream. (.keystore vault))]
(.store keystore fos password))))
(vault-output-stream [vault]
(let [cipher (doto (Cipher/getInstance "AES")
(.init Cipher/ENCRYPT_MODE (vault-key vault)))]
(CipherOutputStream. (io/output-stream (.filename vault)) cipher)))
(vault-input-stream [vault]
(let [cipher (doto (Cipher/getInstance "AES")
(.init Cipher/DECRYPT_MODE (vault-key vault)))]
(CipherInputStream. (io/input-stream (.filename vault)) cipher)))
proto/IOFactory
(make-reader [vault]
(proto/make-reader (vault-input-stream vault)))
(make-writer [vault]
(proto/make-writer (vault-output-stream vault))))
(extend CryptoVault
clojure.java.io/IOFactory
(assoc io/default-streams-impl
:make-input-stream (fn [x opts] (vault-input-stream x))
:make-output-stream (fn [x opts] (vault-output-stream x))))
; records
(defrecord Note [pitch octave duration])
; add keys to records - they are open
(assoc (->Note :D 4 1/2) :velocity 100)
(defprotocol MidiNote
(to-msec [this tempo]))
;can be extended like a datatype
(extend-type Note
MidiNote
(to-msec [this tempo]
(let [duration-to-bpm {1 240, 1/2 120, 1/4 60, 1/8 30, 1/16 15}]
(* 1000 (/ (duration-to-bpm (:duration this))
tempo)))))
; anon datatypes with reify
(let [min-duration 250
min-velocity 64
rand-note (reify
MidiNote
(to-msec [this tempo] (+ rand-int 1000) min-duration))]
(println rand-note "now you have an anon datatype that impl MidiNote"))
; Ch.6 Protocols & Datatypes ;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Ch.7 Macros ;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ` - begin macro
; ~ - insert arg as unquote
; ~@ - splice unquote
(defmacro chain
([x form] `(. ~x ~form))
([x form & more] `(chain (. ~x ~form) ~@more)))
; var# - ensure that there are no naming collisions with local bindings by appending a unique id
(defmacro bench [exp]
`(let [start# (System/nanoTime)
result# ~exp]
{:result result# :elapsed (- (System/nanoTime) start#)}))
; conditional eval
(defmacro and
([] true)
([x] x)
([x & rest]
`(let [and# ~x]
(if and# (and ~@rest) and#))))
; creating vars
(defmacro declare
[& names]
`(do ~@(map #(list 'def %) names)))
; Ch.7 Macros ;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Ch.8 MultiMethods ;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defmulti my-print class)
(defmethod my-print String [s]
(.write *out* s))
(defmethod my-print nil [_]
(.write *out* "nil"))
(defmethod my-print Number [n]
(.write *out* (.toString n)))
(defmethod my-print :default [c]
(.write *out* "#<")
(.write *out* (.toString c))
(.write *out* ">"))
; check type
(isa? Integer Number)
(defmethod my-print java.util.Collection [c]
(.write *out* "(")
(.write *out* (str/join " " c))
(.write *out* ")"))
(defmethod my-print clojure.lang.IPersistentVector [v]
(.write *out* "[")
(.write *out* (str/join " " v))
(.write *out* "]"))
; must resovle vectors bc they are both Collections and IPersistentVectors
(prefer-method my-print clojure.lang.IPersistentVector java.lang.Collection)
; Ad Hoc Taxonomies
(defstruct account :id :tag :balance)
; ::Keyword - keyword in curret ns
::Checking
::Saving
::Premium
::Basic
::Account
; Ad Hoc relationships
(derive ::acc/Saving ::acc/Account)
(derive ::acc/Checking ::acc/Account)
; alias for a shorter name
(ns examples.multimethods.account)
(alias 'acc 'examples.multimethods.account)
(def saving-account (struct account 1 ::acc/Saving 200M))
(def checking-account (struct account 2 ::acc/Checking 100M))
(defmulti interest :tag)
(defmethod interest ::acc/Checking [_] 0M)
(defmethod interest ::acc/Saving [_] 0.05M)
(defmulti account-level :tag)
(defmethod account-level ::acc/Checking [acct]
(if (>= (:balance acct) 5000) ::acc/Premium ::acc/Basic))
(defmethod account-level ::acc/Saving [acct]
(if (>= (:balance acct) 1000) ::acc/Premium ::acc/Basic))
(defmulti service-charge (fn [acct] [(account-level acct) (:tag acct)]))
(defmethod service-charge [::acc/Basic ::acc/Checking [_] 25)
(defmethod service-charge [::acc/Basic ::acc/Saving [_] 10)
(defmethod service-charge [::acc/Premium ::acc/Account [_] 0)
; Ch.8 MultiMethods ;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; LOOKUP
; testing with combinatorics
; test.generative
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment