Created
August 24, 2012 17:26
-
-
Save JHawk/3453165 to your computer and use it in GitHub Desktop.
Programming Clojure Second Edition Notes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
; 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