Permalink
Browse files

dump of REPL interactions and other inline code snippets

  • Loading branch information...
1 parent 4d88963 commit 7c720fea060fc9ebe33b1574ff96caadb723268c @cemerick cemerick committed Apr 3, 2012
View
780 ch01-welcome-repl-interactions.clj
@@ -0,0 +1,780 @@
+
+;-----
+(defn average
+ [numbers]
+ (/ (apply + numbers) (count numbers)))
+
+
+;-----
+(defn average
+ [numbers]
+ (/ (apply + numbers) (count numbers)))
+;= #'user/average
+(average [60 80 100 400])
+;= 160
+
+
+;-----
+(println (average [60 80 100 400]))
+; 160
+;= nil
+
+
+;-----
+(read-string "42")
+;= 42
+(read-string "(+ 1 2)")
+;= (+ 1 2)
+
+
+;-----
+(pr-str [1 2 3])
+;= "[1 2 3]"
+(read-string "[1 2 3]")
+;= [1 2 3]
+
+
+;-----
+"hello there"
+;= "hello there"
+
+
+;-----
+"multiline strings
+are very handy"
+;= "multiline strings\nare very handy"
+
+
+;-----
+(class \c)
+;= java.lang.Character
+
+
+;-----
+\u00ff
+;= \ÿ
+\o41
+;= \!
+
+
+;-----
+(def person {:name "Sandra Cruz"
+ :city "Portland, ME"})
+;= #'user/person
+(:city person)
+;= "Portland, ME"
+
+
+;-----
+(def pizza {:name "Ramunto's"
+ :location "Claremont, NH"
+ ::location "43.3734,-72.3365"})
+;= #'user/pizza
+pizza
+;= {:name "Ramunto's", :location "Claremont, NH", :user/location "43.3734,-72.3365"}
+(:user/location pizza)
+;= "43.3734,-72.3365"
+
+
+;-----
+(name :user/location)
+;= "location"
+(namespace :user/location)
+;= "user"
+(namespace :location)
+;= nil
+
+
+;-----
+(average [60 80 100 400])
+;= 160
+
+
+;-----
+(class #"(p|h)ail")
+;= java.util.regex.Pattern
+
+
+;-----
+(re-seq #"(...) (...)" "foo bar")
+;= (["foo bar" "foo" "bar"])
+
+
+;-----
+(re-seq #"(\d+)-(\d+)" "1-3") ;; would be "(\\d+)-(\\d+)" in Java
+;= (["1-3" "1" "3"])
+
+
+;-----
+(read-string "(+ 1 2 #_(* 2 2) 8)")
+;= (+ 1 2 8)
+
+
+;-----
+(when true
+ (comment (println "hello")))
+;= nil
+
+
+;-----
+(+ 1 2 (comment (* 2 2)) 8)
+;= #<NullPointerException java.lang.NullPointerException>
+
+
+;-----
+(defn silly-adder
+ [x y]
+ (+ x y))
+
+
+;-----
+(defn silly-adder
+ [x, y]
+ (+, x, y))
+
+
+;-----
+(= [1 2 3] [1, 2, 3])
+;= true
+
+
+;-----
+(create-user {:name new-username, :email email})
+
+
+;-----
+'(a b :name 12.5) ;; list
+
+['a 'b :name 12.5] ;; vector
+
+{:name "Chas" :age 31} ;; map
+
+#{1 2 3} ;; set
+
+
+;-----
+(def a 10)
+;= #'user/a
+(defn diff
+ [a b]
+ (- a b))
+;= #'user/diff
+(diff 5 5)
+;= 0
+a
+;= 10
+
+
+;-----
+(def x 1)
+;= #'user/x
+
+
+;-----
+x
+;= 1
+
+
+;-----
+(def x "hello")
+;= #'user/x
+x
+;= "hello"
+
+
+;-----
+*ns*
+;= #<Namespace user>
+(ns foo)
+;= nil
+*ns*
+;= #<Namespace foo>
+user/x
+;= "hello"
+x
+;= #<CompilerException java.lang.RuntimeException:
+;= Unable to resolve symbol: x in this context, compiling:(NO_SOURCE_PATH:0)>
+
+
+;-----
+String
+;= java.lang.String
+Integer
+;= java.lang.Integer
+java.util.List
+;= java.util.List
+java.net.Socket
+;= java.net.Socket
+
+
+;-----
+filter
+;= #<core$filter clojure.core$filter@7444f787>
+
+
+;-----
+(defn average
+ [numbers]
+ (/ (apply + numbers) (count numbers)))
+
+
+;-----
+(average [60 80 100 400])
+;= 160
+
+
+;-----
+(quote x)
+;= x
+(symbol? (quote x))
+;= true
+
+
+;-----
+'x
+;= x
+
+
+;-----
+'(+ x x)
+;= (+ x x)
+(list? '(+ x x))
+;= true
+
+
+;-----
+(list '+ 'x 'x)
+;= (+ x x)
+
+
+;-----
+''x
+;= (quote x)
+
+
+;-----
+'@x
+;= (clojure.core/deref x)
+'#(+ % %)
+;= (fn* [p1__3162792#] (+ p1__3162792# p1__3162792#))
+'`(a b ~c)
+;= (seq (concat (list (quote user/a))
+;= (list (quote user/b))
+;= (list c)))
+
+
+;-----
+(do
+ (println "hi")
+ (apply * [4 5 6]))
+; hi
+;= 120
+
+
+;-----
+(let [a (inc (rand-int 6))
+ b (inc (rand-int 6))]
+ (println (format "You rolled a %s and a %s" a b))
+ (+ a b))
+
+
+;-----
+(let [a (inc (rand-int 6))
+ b (inc (rand-int 6))]
+ (do
+ (println (format "You rolled a %s and a %s" a b))
+ (+ a b)))
+
+
+;-----
+(def p "foo")
+;= #'user/p
+p
+;= "foo"
+
+
+
+;-----
+(defn hypot
+ [x y]
+ (let [x2 (* x x)
+ y2 (* y y)]
+ (Math/sqrt (+ x2 y2))))
+
+
+;-----
+(def v [42 "foo" 99.2 [5 12]])
+;= #'user/v
+
+
+;-----
+(first v)
+;= 42
+(second v)
+;= "foo"
+(last v)
+;= [5 12]
+(nth v 2)
+;= 99.2
+(v 2)
+;= 99.2
+(.get v 2)
+;= 99.2
+
+
+;-----
+(+ (first v) (v 2))
+;= 141.2
+
+
+;-----
+(+ (first v) (first (last v)))
+;= 47
+
+
+;-----
+(def v [42 "foo" 99.2 [5 12]])
+;= #'user/v
+(let [[x y z] v]
+ (+ x z))
+;= 141.2
+
+
+;-----
+(let [x (nth v 0)
+ y (nth v 1)
+ z (nth v 2)]
+ (+ x z))
+;= 141.2
+
+
+
+;-----
+[x y z]
+[42 "foo" 99.2 [5 12]]
+
+
+;-----
+(let [[x _ _ [y z]] v]
+ (+ x y z))
+;= 59
+
+
+;-----
+[x _ _ [y z ]]
+[42 "foo" 99.2 [5 12]]
+
+
+;-----
+(let [[x & rest] v]
+ rest)
+;= ("foo" 99.2 [5 12])
+
+
+;-----
+(let [[x _ z :as original-vector] v]
+ (conj original-vector (+ x z)))
+;= [42 "foo" 99.2 [5 12] 141.2]
+
+
+;-----
+(def m {:a 5 :b 6
+ :c [7 8 9]
+ :d {:e 10 :f 11}
+ "foo" 88
+ 42 false})
+;= #'user/m
+(let [{a :a b :b} m]
+ (+ a b))
+;= 11
+
+
+;-----
+{a :a b :b}
+{:a 5 :b 6}
+
+
+;-----
+(let [{f "foo"} m]
+ (+ f 12))
+;= 100
+(let [{v 42} m]
+ (if v 1 0))
+;= 0
+
+
+;-----
+(let [{x 3 y 8} [12 0 0 -18 44 6 0 0 1]]
+ (+ x y))
+;= -17
+
+
+;-----
+(let [{{e :e} :d} m]
+ (* 2 e))
+;= 20
+
+
+;-----
+(let [{[x _ y] :c} m]
+ (+ x y))
+;= 16
+(def map-in-vector ["James" {:birthday (java.util.Date. 73 1 6)}])
+;= #'user/map-in-vector
+(let [[name {bd :birthday}] map-in-vector]
+ (str name " was born on " bd))
+;= "James was born on Thu Feb 06 00:00:00 EST 1973"
+
+
+;-----
+(let [{r1 :x r2 :y :as randoms}
+ (zipmap [:x :y :z] (repeatedly (partial rand-int 10)))]
+ (assoc randoms :sum (+ r1 r2)))
+;= {:sum 17, :z 3, :y 8, :x 9}
+
+
+;-----
+(let [{k :unknown x :a
+ :or {k 50}} m]
+ (+ k x))
+;= 55
+
+
+;-----
+(let [{k :unknown x :a} m
+ k (or k 50)]
+ (+ k x))
+;= 55
+
+
+;-----
+(let [{opt1 :option} {:option false}
+ opt1 (or opt1 true)
+ {opt2 :option :or {opt2 true}} {:option false}]
+ {:opt1 opt1 :opt2 opt2})
+;= {:opt1 true, :opt2 false}
+
+
+;-----
+(def chas {:name "Chas" :age 31 :location "Massachusetts"})
+;= #'user/chas
+(let [{name :name age :age location :location} chas]
+ (format "%s is %s years old and lives in %s." name age location))
+;= "Chas is 31 years old and lives in Massachusetts."
+
+
+;-----
+(let [{:keys [name age location]} chas]
+ (format "%s is %s years old and lives in %s." name age location))
+;= "Chas is 31 years old and lives in Massachusetts."
+
+
+;-----
+(def brian {"name" "Brian" "age" 31 "location" "British Columbia"})
+;= #'user/brian
+(let [{:strs [name age location]} brian]
+ (format "%s is %s years old and lives in %s." name age location))
+;= "Brian is 31 years old and lives in British Columbia."
+
+(def christophe {'name "Christophe" 'age 33 'location "Rhône-Alpes"})
+;= #'user/christophe
+(let [{:syms [name age location]} christophe]
+ (format "%s is %s years old and lives in %s." name age location))
+;= "Christophe is 31 years old and lives in Rhône-Alpes."
+
+
+;-----
+(def user-info ["robert8990" 2011 :name "Bob" :city "Boston"])
+;= #'user/user-info
+
+
+;-----
+(let [[username account-year & extra-info] user-info
+ {:keys [name city]} (apply hash-map extra-info)]
+ (format "%s is in %s" name city))
+;= "Bob is in Boston"
+
+
+;-----
+(let [[username account-year & {:keys [name city]}] user-info]
+ (format "%s is in %s" name city))
+;= "Bob is in Boston"
+
+
+;-----
+(fn [x]
+ (+ 10 x))
+
+
+;-----
+((fn [x] (+ 10 x)) 8)
+;= 18
+
+
+;-----
+(let [x 8]
+ (+ 10 x))
+
+
+;-----
+((fn [x y z] (+ x y z))
+ 3 4 12)
+;= 19
+
+
+;-----
+(let [x 3
+ y 4
+ z 12]
+ (+ x y z))
+
+
+;-----
+(def strange-adder (fn adder-self-reference
+ ([x] (adder-self-reference x 1))
+ ([x y] (+ x y))))
+;= #'user/strange-adder
+(strange-adder 10)
+;= 11
+(strange-adder 10 50)
+;= 60
+
+
+;-----
+(letfn [(odd? [n]
+ (even? (dec n)))
+ (even? [n]
+ (or (zero? n)
+ (odd? (dec n))))]
+ (odd? 11))
+;= true
+
+
+;-----
+(def strange-adder (fn strange-adder
+ ([x] (strange-adder x 1))
+ ([x y] (+ x y))))
+
+(defn strange-adder
+ ([x] (strange-adder x 1))
+ ([x y] (+ x y))))
+
+
+;-----
+(def redundant-adder (fn redundant-adder
+ [x y z]
+ (+ x y z)))
+
+(defn redundant-adder
+ [x y z]
+ (+ x y z))
+
+
+;-----
+(defn concat-rest
+ [x & rest]
+ (apply str (butlast rest)))
+;= #'user/concat-rest
+(concat-rest 0 1 2 3 4)
+;= "123"
+
+
+;-----
+(defn make-user
+ [& [user-id]]
+ {:user-id (or user-id
+ (str (java.util.UUID/randomUUID)))})
+;= #'user/make-user
+(make-user)
+;= {:user-id "ef165515-6d6f-49d6-bd32-25eeb024d0b4"}
+(make-user "Bobby")
+;= {:user-id "Bobby"}
+
+
+;-----
+(defn make-user
+ [username & {:keys [email join-date]
+ :or {join-date (java.util.Date.)}}]
+ {:username username
+ :join-date join-date
+ :email email
+ ;; 2.592e9 -> one month in ms
+ :exp-date (java.util.Date. (long (+ 2.592e9 (.getTime join-date))))})
+;= #'user/make-user
+(make-user "Bobby")
+;= {:username "Bobby", :join-date #<Date Mon Jan 09 16:56:16 EST 2012>,
+;= :email nil, :exp-date #<Date Wed Feb 08 16:56:16 EST 2012>}
+(make-user "Bobby"
+ :join-date (java.util.Date. 111 0 1)
+ :email "bobby@example.com")
+;= {:username "Bobby", :join-date #<Date Sun Jan 01 00:00:00 EST 2011>,
+;= :email "bobby@example.com", :exp-date #<Date Tue Jan 31 00:00:00 EST 2011>}
+
+
+;-----
+(defn foo
+ [& {k ["m" 9]}]
+ (inc k))
+;= #'user/foo
+(foo ["m" 9] 19)
+;= 20
+
+
+;-----
+(fn [x y] (Math/pow x y))
+
+#(Math/pow %1 %2)
+
+
+;-----
+(read-string "#(Math/pow %1 %2)")
+;= (fn* [p1__285# p2__286#] (Math/pow p1__285# p2__286#))
+
+
+;-----
+(fn [x y]
+ (println (str x \^ y))
+ (Math/pow x y))
+
+
+;-----
+#(do (println (str %1 \^ %2))
+ (Math/pow %1 %2))
+
+
+;-----
+(fn [x & rest]
+ (- x (apply + rest)))
+
+#(- % (apply + %&))
+
+
+;-----
+(fn [x]
+ (fn [y]
+ (+ x y)))
+
+
+;-----
+#(#(+ % %))
+;= #<IllegalStateException java.lang.IllegalStateException:
+;= Nested #()s are not allowed>
+
+
+;-----
+(if "hi" \t)
+;= \t
+(if 42 \t)
+;= \t
+(if nil "unevaluated" \f)
+;= \f
+(if false "unevaluated" \f)
+;= \f
+(if (not true) \t)
+;= nil
+
+
+;-----
+(true? "string")
+;= false
+(if "string" \t \f)
+;= \t
+
+
+;-----
+(loop [x 5]
+ (if (neg? x)
+ x
+ (recur (dec x))))
+;= -1
+
+
+;-----
+(defn countdown
+ [x]
+ (if (zero? x)
+ :blastoff!
+ (do (println x)
+ (recur (dec x)))))
+;= #'user/countdown
+(countdown 5)
+; 5
+; 4
+; 3
+; 2
+; 1
+;= :blastoff!
+
+
+;-----
+(def x 5)
+;= #'user/x
+x
+;= 5
+
+
+;-----
+(var x)
+;= #'user/x
+
+
+;-----
+#'x
+;= #'user/x
+
+
+;-----
+(defn average
+ [numbers]
+ (/ (apply + numbers) (count numbers)))
+
+
+;-----
+(def average (fn average
+ [numbers]
+ (/ (apply + numbers) (count numbers))))
+
+
+;-----
+(eval :foo)
+;= :foo
+(eval [1 2 3])
+;= [1 2 3]
+(eval "text")
+;= "text"
+
+
+;-----
+(eval '(average [60 80 100 400]))
+;= 160
+
+
+;-----
+(eval (read-string "(average [60 80 100 400])"))
+;= 160
+
+
+;-----
+(defn embedded-repl
+ "A naive Clojure REPL implementation. Enter `:quit`
+ to exit."
+ []
+ (print (str (ns-name *ns*) ">>> "))
+ (flush)
+ (let [expr (read)
+ value (eval expr)]
+ (when (not= :quit value)
+ (println value)
+ (recur))))
+
+(embedded-repl)
+; user>>> (defn average2
+; [numbers]
+; (/ (apply + numbers) (count numbers)))
+; #'user/average2
+; user>>> (average2 [3 7 5])
+; 5
+; user>>> :quit
+;= nil
+
+
View
465 ch02-FP-repl-interactions.clj
@@ -0,0 +1,465 @@
+;-----
+true false 5 14.2 \T "hello" nil
+
+
+;-----
+(= 5 5)
+
+(= 5 (+ 2 3))
+
+(= "boot" (str "bo" "ot"))
+
+(= nil nil)
+
+(let [a 5]
+ (do-something-with-a-number a)
+ (= a 5))
+
+
+;-----
+public class StatefulInteger extends Number {
+ private int state;
+
+ public StatefulInteger (int initialState) {
+ this.state = initialState;
+ }
+
+ public void setInt (int newState) {
+ this.state = newState;
+ }
+
+ public int intValue () {
+ return state;
+ }
+
+ public int hashCode () {
+ return state;
+ }
+
+ public boolean equals (Object obj) {
+ return obj instanceof StatefulInteger &&
+ state == ((StatefulInteger)obj).state;
+ }
+
+ // remaining xxxValue() methods from java.lang.Number...
+}
+
+
+;-----
+(def five (StatefulInteger. 5))
+;= #'user/five
+(def six (StatefulInteger. 6))
+;= #'user/six
+(.intValue five)
+;= 5
+(= five six)
+;= false
+(.setInt five 6)
+;= nil
+(= five six)
+;= true
+
+
+;-----
+(defn print-number
+ [n]
+ (println (.intValue n))
+ (.setInt n 42))
+;= #'user/print-number
+(print-number six)
+; 6
+;= nil
+(= five six)
+;= false
+(= five (StatefulInteger. 42))
+;= true
+
+
+
+;-----
+(def h {[1 2] 3})
+;= #'user/h
+(h [1 2])
+;= 3
+(conj (first (keys h)) 3)
+;= [1 2 3]
+(h [1 2])
+;= 3
+h
+;= {[1 2] 3}
+
+
+;-----
+(defn call-twice [f x]
+ (f x)
+ (f x))
+
+(call-twice println 123)
+; 123
+; 123
+
+
+
+;-----
+(max 5 6)
+;= 6
+(require 'clojure.string)
+;= nil
+(clojure.string/lower-case "Clojure")
+;= "clojure"
+
+
+;-----
+(map clojure.string/lower-case ["Java" "Imperative" "Weeping"
+ "Clojure" "Learning" "Peace"])
+;= ("java" "imperative" "weeping" "clojure" "learning" "peace")
+(map * [1 2 3 4] [5 6 7 8])
+;= (5 12 21 32)
+
+
+;-----
+(reduce max [0 -3 10 48])
+;= 10
+
+
+;-----
+(max 0 -3)
+;= 0
+(max 0 10)
+;= 10
+(max 10 48)
+;= 48
+
+
+;-----
+(max (max (max 0 -3) 10) 48)
+;= 48
+
+
+;-----
+(reduce + 50 [1 2 3 4])
+;= 60
+
+
+;-----
+(reduce
+ (fn [m v]
+ (assoc m v (* v v)))
+ {}
+ [1 2 3 4])
+;= {4 16, 3 9, 2 4, 1 1}
+
+
+;-----
+(reduce
+ #(assoc % %2 (* %2 %2))
+ {}
+ [1 2 3 4])
+;= {4 16, 3 9, 2 4, 1 1}
+
+
+;-----
+((complement pos?) 5)
+;= false
+((complement string?) "hello")
+;= false
+((complement string?) :hello)
+;= true
+
+
+;-----
+(take 10 (repeatedly #(rand-int 10)))
+;= (5 3 5 5 9 0 8 0 1 0)
+(take 3 (repeatedly (fn []
+ (Thread/sleep 1000)
+ (System/currentTimeMillis))))
+;= (1322663857960 1322663858961 1322663859961)
+
+
+
+;-----
+(apply hash-map [:a 5 :b 6])
+;= {:a 5, :b 6}
+
+
+;-----
+(def args [2 -2 10])
+;= #'user/args
+(apply * 0.5 3 args)
+;= -60.0
+
+
+
+;-----
+(def only-strings (partial filter string?))
+;= #'user/only-strings
+(only-strings ["a" 5 "b" 6])
+;= ("a" "b")
+
+
+;-----
+(def database-lookup (partial get-data "jdbc:mysql://..."))
+
+
+;-----
+(#(filter string? %) ["a" 5 "b" 6])
+;= ("a" "b")
+
+
+;-----
+(#(filter % ["a" 5 "b" 6]) string?)
+;= ("a" "b")
+(#(filter % ["a" 5 "b" 6]) number?)
+;= (5 6)
+
+
+;-----
+(#(map *) [1 2 3] [4 5 6] [7 8 9])
+;= #<ArityException clojure.lang.ArityException:
+;= Wrong number of args (3) passed to: user$eval812$fn>
+(#(map * % %2 %3) [1 2 3] [4 5 6] [7 8 9])
+;= (28 80 162)
+(#(map * % %2 %3) [1 2 3] [4 5 6])
+;= #<ArityException clojure.lang.ArityException:
+;= Wrong number of args (2) passed to: user$eval843$fn>
+(#(apply map * %&) [1 2 3] [4 5 6] [7 8 9])
+;= (28 80 162)
+(#(apply map * %&) [1 2 3])
+;= (1 2 3)
+
+((partial map *) [1 2 3] [4 5 6] [7 8 9])
+;= (28 80 162)
+
+
+;-----
+(defn negated-sum-str
+ [& numbers]
+ (str (- (apply + numbers))))
+;= #'user/negated-sum-str
+(negated-sum-str 10 12 3.4)
+;= "-25.4"
+
+
+;-----
+(def negated-sum-str (comp str - +))
+;= #'user/negated-sum-str
+(negated-sum-str 10 12 3.4)
+;= "-25.4"
+
+
+;-----
+((comp + - str) 5 10)
+;= #<ClassCastException java.lang.ClassCastException:
+;= java.lang.String cannot be cast to java.lang.Number>
+
+
+;-----
+(require '[clojure.string :as str])
+
+(def camel->keyword (comp keyword
+ str/join
+ (partial interpose \-)
+ (partial map str/lower-case)
+ #(str/split % #"(?<=[a-z])(?=[A-Z])")))
+;= #'user/camel->keyword
+(camel->keyword "CamelCase")
+;= :camel-case
+(camel->keyword "lowerCamelCase")
+;= :lower-camel-case
+
+
+;-----
+(defn camel->keyword
+ [s]
+ (->> (str/split s #"(?<=[a-z])(?=[A-Z])")
+ (map str/lower-case)
+ (interpose \-)
+ str/join
+ keyword))
+
+
+;-----
+(def camel-pairs->map (comp (partial apply hash-map)
+ (partial map-indexed (fn [i x]
+ (if (odd? i)
+ x
+ (camel->keyword x))))))
+;= #'user/camel-pairs->map
+(camel-pairs->map ["CamelCase" 5 "lowerCamelCase" 3])
+;= {:camel-case 5, :lower-camel-case 3}
+
+
+;-----
+(defn adder
+ [n]
+ (fn [x] (+ n x)))
+;= #'user/adder
+((adder 5) 18)
+;= 23
+
+
+;-----
+(defn doubler
+ [f]
+ (fn [& args]
+ (* 2 (apply f args))))
+;= #'user/doubler
+(def double-+ (doubler +))
+;= #'user/double-+
+(double-+ 1 2 3)
+;= 12
+
+
+;-----
+(defn print-logger
+ [writer]
+ #(binding [*out* writer]
+ (println %)))
+
+
+;-----
+(def *out*-logger (print-logger *out*))
+;= #'user/*out*-logger
+(*out*-logger "hello")
+; hello
+;= nil
+
+
+;-----
+(def writer (java.io.StringWriter.))
+;= #'user/writer
+(def retained-logger (print-logger writer))
+;= #'user/retained-logger
+(retained-logger "hello")
+;= nil
+(str writer)
+;= "hello\n"
+
+
+;-----
+(require 'clojure.java.io)
+
+(defn file-logger
+ [file]
+ #(with-open [f (clojure.java.io/writer file :append true)]
+ ((print-logger f) %)))
+
+
+;-----
+(def log->file (file-logger "messages.log"))
+;= #'user/log->file
+(log->file "hello")
+;= nil
+
+% more messages.log
+hello
+
+
+;-----
+(defn multi-logger
+ [& logger-fns]
+ #(doseq [f logger-fns]
+ (f %)))
+
+
+;-----
+(def log (multi-logger
+ (print-logger *out*)
+ (file-logger "messages.log")))
+;= #'user/log
+(log "hello again")
+; hello again
+;= nil
+
+% more messages.log
+hello
+hello again
+
+
+;-----
+(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"))))
+
+(log-timestamped "goodbye, now")
+; [2011-11-30 08:54:00] goodbye, now
+;= nil
+
+% more messages.log
+hello
+hello again
+[2011-11-30 08:54:00] goodbye, now
+
+
+;-----
+(defn perform-bank-transfer!
+ [from-account to-account amount]
+ ...)
+
+(defn authorize-medical-treatment!
+ [patient-id treatment-id]
+ ...)
+
+(defn launch-missiles!
+ [munition-type target-coordinates]
+ ...)
+
+
+;-----
+(require 'clojure.xml)
+
+(defn twitter-followers
+ [username]
+ (->> (str "https://api.twitter.com/1/users/show.xml?screen_name=" username)
+ clojure.xml/parse
+ :content
+ (filter (comp #{:followers_count} :tag))
+ first
+ :content
+ first
+ Integer/parseInt))
+
+(twitter-followers "ClojureBook")
+;= 106
+(twitter-followers "ClojureBook")
+;= 107
+
+
+;-----
+(+ 1 2) (- 10 7) (count [-1 0 1])
+
+
+;-----
+(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))
+; "Elapsed time: 2181.014 msecs"
+;= true
+(let [m-prime? (memoize prime?)]
+ (time (m-prime? 1125899906842679))
+ (time (m-prime? 1125899906842679)))
+; "Elapsed time: 2085.029 msecs"
+; "Elapsed time: 0.042 msecs"
+;= true
+
+
+;-----
+(repeatedly 10 (partial rand-int 10))
+;= (3 0 2 9 8 8 5 7 3 5)
+(repeatedly 10 (partial (memoize rand-int) 10))
+;= (4 4 4 4 4 4 4 4 4 4)
+
+
View
1,701 ch03-collections-repl-interactions.clj
@@ -0,0 +1,1701 @@
+;-----
+'(a b :name 12.5) ;; list
+
+['a 'b :name 12.5] ;; vector
+
+{:name "Chas" :age 31} ;; map
+
+#{1 2 3} ;; set
+
+{Math/PI "~3.14"
+ [:composite "key"] 42
+ nil "nothing"} ;; another map
+
+#{{:first-name "chas" :last-name "emerick"}
+ {:first-name "brian" :last-name "carper"}
+ {:first-name "christophe" :last-name "grand"}} ;; a set of maps
+
+
+;-----
+(def v [1 2 3])
+;= #'user/v
+(conj v 4)
+;= [1 2 3 4]
+(conj v 4 5)
+;= [1 2 3 4 5]
+(seq v)
+;= (1 2 3)
+
+
+;-----
+(def m {:a 5 :b 6})
+;= #'user/m
+(conj m [:c 7])
+;= {:a 5, :c 7, :b 6}
+(seq m)
+;= ([:a 5] [:b 6])
+
+
+;-----
+(def s #{1 2 3})
+;= #'user/s
+(conj s 10)
+;= #{1 2 3 10}
+(conj s 3 4)
+;= #{1 2 3 4}
+(seq s)
+;= (1 2 3)
+
+
+;-----
+(def lst '(1 2 3))
+;= #'user/lst
+(conj lst 0)
+;= (0 1 2 3)
+(conj lst 0 -1)
+;= (-1 0 1 2 3)
+(seq lst)
+;= (1 2 3)
+
+
+;-----
+(into v [4 5])
+;= [1 2 3 4 5]
+(into m [[:c 7] [:d 8]])
+;= {:a 5, :c 7, :b 6, :d 8}
+(into #{1 2} [2 3 4 5 3 3 2])
+;= #{1 2 3 4 5}
+(into [1] {:a 1 :b 2})
+;= [1 [:a 1] [:b 2]]
+
+
+;-----
+(conj '(1 2 3) 4)
+;= (4 1 2 3)
+(into '(1 2 3) [:a :b :c])
+;= (:c :b :a 1 2 3)
+
+
+;-----
+(defn swap-pairs
+ [sequential]
+ (into (empty sequential)
+ (interleave
+ (take-nth 2 (drop 1 sequential))
+ (take-nth 2 sequential))))
+
+(swap-pairs (apply list (range 10)))
+;= (8 9 6 7 4 5 2 3 0 1)
+(swap-pairs (apply vector (range 10)))
+;= [1 0 3 2 5 4 7 6 9 8]
+
+
+;-----
+(defn map-map
+ [f m]
+ (into (empty m)
+ (for [[k v] m]
+ [k (f v)])))
+
+
+;-----
+(map-map inc (hash-map :z 5 :c 6 :a 0))
+;= {:z 6, :a 1, :c 7}
+(map-map inc (sorted-map :z 5 :c 6 :a 0))
+;= {:a 1, :c 7, :z 6}
+
+
+;-----
+(count [1 2 3])
+;= 3
+(count {:a 1 :b 2 :c 3})
+;= 3
+(count #{1 2 3})
+;= 3
+(count '(1 2 3))
+;= 3
+
+
+;-----
+(seq "Clojure")
+;= (\C \l \o \j \u \r \e)
+(seq {:a 5 :b 6})
+;= ([:a 5] [:b 6])
+(seq (java.util.ArrayList. (range 5)))
+;= (0 1 2 3 4)
+(seq (into-array ["Clojure" "Programming"]))
+;= ("Clojure" "Programming")
+(seq [])
+;= nil
+(seq nil)
+;= nil
+
+
+;-----
+(map str "Clojure")
+;= ("C" "l" "o" "j" "u" "r" "e")
+(set "Programming")
+;= #{\a \g \i \m \n \o \P \r}
+
+
+;-----
+(first "Clojure")
+;= \C
+(rest "Clojure")
+;= (\l \o \j \u \r \e)
+(next "Clojure")
+;= (\l \o \j \u \r \e)
+
+
+;-----
+(rest [1])
+;= ()
+(next [1])
+;= nil
+(rest nil)
+;= ()
+(next nil)
+;= nil
+
+
+;-----
+(= (next x)
+ (seq (rest x)))
+
+
+;-----
+(doseq [x (range 3)]
+ (println x))
+; 0
+; 1
+; 2
+
+
+;-----
+(let [r (range 3)
+ rst (rest r)]
+ (prn (map str rst))
+ (prn (map #(+ 100 %) r))
+ (prn (conj r -1) (conj rst 42)))
+; ("1" "2")
+; (100 101 102)
+; (-1 0 1 2) (42 1 2)
+
+
+;-----
+(let [s (range 1e6)]
+ (time (count s)))
+; "Elapsed time: 147.661 msecs"
+;= 1000000
+(let [s (apply list (range 1e6))]
+ (time (count s)))
+; "Elapsed time: 0.03 msecs"
+;= 1000000
+
+
+;-----
+(cons 0 (range 1 5))
+;= (0 1 2 3 4)
+
+
+;-----
+(cons :a [:b :c :d])
+;= (:a :b :c :d)
+
+
+;-----
+(cons 0 (cons 1 (cons 2 (cons 3 (range 4 10)))))
+;= (0 1 2 3 4 5 6 7 8 9)
+(list* 0 1 2 3 (range 4 10))
+;= (0 1 2 3 4 5 6 7 8 9)
+
+
+;-----
+(lazy-seq [1 2 3])
+;= (1 2 3)
+
+
+;-----
+(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))
+;= (32 37 8 2 22 41 19 27 34 27)
+
+
+;-----
+(defn random-ints
+ [limit]
+ (lazy-seq
+ (println "realizing random number")
+ (cons (rand-int limit)
+ (random-ints limit))))
+
+(def rands (take 10 (random-ints 50)))
+;= #'user/rands
+(first rands)
+; realizing random number
+;= 39
+(nth rands 3)
+; realizing random number
+; realizing random number
+; realizing random number
+;= 44
+(count rands)
+; realizing random number
+; realizing random number
+; realizing random number
+; realizing random number
+; realizing random number
+; realizing random number
+;= 10
+(count rands)
+;= 10
+
+
+;-----
+(repeatedly 10 (partial rand-int 50))
+;= (47 19 26 14 18 37 44 13 41 38)
+
+
+;-----
+(def x (next (random-ints 50)))
+; realizing random number
+; realizing random number
+
+
+;-----
+(def x (rest (random-ints 50)))
+; realizing random number
+
+
+;-----
+(let [[x & rest] (random-ints 50)])
+; realizing random number
+; realizing random number
+;= nil
+
+
+;-----
+(dorun (take 5 (random-ints 50)))
+; realizing random number
+; realizing random number
+; realizing random number
+; realizing random number
+; realizing random number
+;= nil
+
+
+;-----
+(doc iterate)
+; -------------------------
+; clojure.core/iterate
+; ([f x])
+; Returns a lazy sequence of x, (f x), (f (f x)) etc.
+; f must be free of side-effects
+
+(doc reverse)
+; -------------------------
+; clojure.core/reverse
+; ([coll])
+; Returns a seq of the items in coll in reverse order. Not lazy.
+
+
+;-----
+(apply str (remove (set "aeiouy")
+ "vowels are useless! or maybe not..."))
+;= "vwls r slss! r mb nt..."
+
+
+;-----
+(split-with neg? (range -5 5))
+;= [(-5 -4 -3 -2 -1) (0 1 2 3 4)]
+
+
+;-----
+(let [[t d] (split-with #(< % 12) (range 1e8))]
+ [(count d) (count t)])
+;= #<OutOfMemoryError java.lang.OutOfMemoryError: Java heap space>
+
+
+;-----
+(let [[t d] (split-with #(< % 12) (range 1e8))]
+ [(count t) (count d)])
+;= [12 99999988]
+
+
+;-----
+(def m {:a 1, :b 2, :c 3})
+;= #'user/m
+(get m :b)
+;= 2
+(get m :d)
+;= nil
+(get m :d "not-found")
+;= "not-found"
+(assoc m :d 4)
+;= {:a 1, :b 2, :c 3, :d 4}
+(dissoc m :b)
+;= {:a 1, :c 3}
+
+
+;-----
+(assoc m
+ :x 4
+ :y 5
+ :z 6)
+;= {:z 6, :y 5, :x 4, :a 1, :c 3, :b 2}
+(dissoc m :a :c)
+;= {:b 2}
+
+
+;-----
+(def v [1 2 3])
+;= #'user/v
+(get v 1)
+;= 2
+(get v 10)
+;= nil
+(get v 10 "not-found")
+;= "not-found"
+(assoc v
+ 1 4
+ 0 -12
+ 2 :p)
+;= [-12 4 :p]
+
+
+;-----
+(assoc v 3 10)
+;= [1 2 3 10]
+
+
+;-----
+(get #{1 2 3} 2)
+;= 2
+(get #{1 2 3} 4)
+;= nil
+(get #{1 2 3} 4 "not-found")
+;= "not-found"
+
+
+;-----
+(when (get #{1 2 3} 2)
+ (println "it contains `2`!"))
+; it contains `2`!
+
+
+;-----
+(contains? [1 2 3] 0)
+;= true
+(contains? {:a 5 :b 6} :b)
+;= true
+(contains? {:a 5 :b 6} 42)
+;= false
+(contains? #{1 2 3} 1)
+;= true
+
+
+;-----
+(contains? [1 2 3] 3)
+;= false
+(contains? [1 2 3] 2)
+;= true
+(contains? [1 2 3] 0)
+;= true
+
+
+;-----
+(get "Clojure" 3)
+;= \j
+(contains? (java.util.HashMap.) "not-there")
+;= false
+(get (into-array [1 2 3]) 0)
+;= 1
+
+
+;-----
+(get {:ethel nil} :lucy)
+;= nil
+(get {:ethel nil} :ethel)
+;= nil
+
+
+;-----
+(find {:ethel nil} :lucy)
+;= nil
+(find {:ethel nil} :ethel)
+;= [:ethel nil]
+
+
+;-----
+(if-let [e (find {:a 5 :b 6} :a)]
+ (format "found %s => %s" (key e) (val e))
+ "not found")
+;= "found :a => 5"
+(if-let [[k v] (find {:a 5 :b 6} :a)]
+ (format "found %s => %s" k v)
+ "not found")
+;= "found :a => 5"
+
+
+;-----
+(nth [:a :b :c] 2)
+;= :c
+(get [:a :b :c] 2)
+;= :c
+(nth [:a :b :c] 3)
+;= java.lang.IndexOutOfBoundsException
+(get [:a :b :c] 3)
+;= nil
+(nth [:a :b :c] -1)
+;= java.lang.IndexOutOfBoundsException
+(get [:a :b :c] -1)
+;= nil
+
+
+;-----
+(nth [:a :b :c] -1 :not-found)
+;= :not-found
+(get [:a :b :c] -1 :not-found)
+;= :not-found
+
+
+;-----
+(get 42 0)
+;= nil
+(nth 42 0)
+;= java.lang.UnsupportedOperationException: nth not supported on this type: Long
+
+
+;-----
+(conj '() 1)
+;= (1)
+(conj '(2 1) 3)
+;= (3 2 1)
+(peek '(3 2 1))
+;= 3
+(pop '(3 2 1))
+;= (2 1)
+(pop '(1))
+;= ()
+
+
+;-----
+(conj [] 1)
+;= [1]
+(conj [1 2] 3)
+;= [1 2 3]
+(peek [1 2 3])
+;= 3
+(pop [1 2 3])
+;= [1 2]
+(pop [1])
+;= []
+
+
+;-----
+(get #{1 2 3} 2)
+;= 2
+(get #{1 2 3} 4)
+;= nil
+(get #{1 2 3} 4 "not-found")
+;= "not-found"
+
+
+;-----
+(disj #{1 2 3} 3 1)
+;= #{2}
+
+
+;-----
+(def sm (sorted-map :z 5 :x 9 :y 0 :b 2 :a 3 :c 4))
+;= #'user/sm
+sm
+;= {:a 3, :b 2, :c 4, :x 9, :y 0, :z 5}
+(rseq sm)
+;= ([:z 5] [:y 0] [:x 9] [:c 4] [:b 2] [:a 3])
+(subseq sm <= :c)
+;= ([:a 3] [:b 2] [:c 4])
+(subseq sm > :b <= :y)
+;= ([:c 4] [:x 9] [:y 0])
+(rsubseq sm > :b <= :y)
+;= ([:y 0] [:x 9] [:c 4])
+
+
+;-----
+(compare 2 2)
+;= 0
+(compare "ab" "abc")
+;= -1
+(compare ["a" "b" "c"] ["a" "b"])
+;= 1
+(compare ["a" 2] ["a" 2 0])
+;= -1
+
+
+;-----
+(sort < (repeatedly 10 #(rand-int 100)))
+;= (12 16 22 23 41 42 61 63 83 87)
+(sort-by first > (map-indexed vector "Clojure"))
+;= ([6 \e] [5 \r] [4 \u] [3 \j] [2 \o] [1 \l] [0 \C])
+
+
+;-----
+((comparator <) 1 4)
+;= -1
+((comparator <) 4 1)
+;= 1
+((comparator <) 4 4)
+;= 0
+
+
+;-----
+(sorted-map-by compare :z 5 :x 9 :y 0 :b 2 :a 3 :c 4)
+;= {:a 3, :b 2, :c 4, :x 9, :y 0, :z 5}
+(sorted-map-by (comp - compare) :z 5 :x 9 :y 0 :b 2 :a 3 :c 4)
+;= {:z 5, :y 0, :x 9, :c 4, :b 2, :a 3}
+
+
+;-----
+(defn magnitude
+ [x]
+ (-> x Math/log10 Math/floor))
+;= #'user/magnitude
+(magnitude 100)
+;= 2.0
+(magnitude 100000)
+;= 5.0
+
+
+;-----
+(defn compare-magnitude
+ [a b]
+ (- (magnitude a) (magnitude b)))
+
+((comparator compare-magnitude) 10 10000)
+;= -1
+((comparator compare-magnitude) 100 10)
+;= 1
+((comparator compare-magnitude) 10 75)
+;= 0
+
+
+;-----
+(sorted-set-by compare-magnitude 10 1000 500)
+;= #{10 500 1000}
+(conj *1 600)
+;= #{10 500 1000}
+(disj *1 750)
+;= #{10 1000}
+(contains? *1 1239)
+;= true
+
+
+;-----
+(defn compare-magnitude
+ [a b]
+ (let [diff (- (magnitude a) (magnitude b))]
+ (if (zero? diff)
+ (compare a b)
+ diff)))
+
+(sorted-set-by compare-magnitude 10 1000 500)
+;= #{10 500 1000}
+(conj *1 600)
+;= #{10 500 600 1000}
+(disj *1 750)
+;= #{10 500 600 1000}
+
+
+;-----
+(sorted-set-by compare-magnitude 10 1000 500 670 1239)
+;= #{10 500 670 1000 1239}
+(def ss *1)
+;= #'user/ss
+(subseq ss > 500)
+;= (670 1000 1239)
+(subseq ss > 500 <= 1000)
+;= (670 1000)
+(rsubseq ss > 500 <= 1000)
+;= (1000 670)
+
+
+;-----
+(defn interpolate
+ "Takes a collection of points (as [x y] tuples), returning a function
+ which is a linear interpolation between those points."
+ [points]
+ (let [results (into (sorted-map) (map vec points))]
+ (fn [x]
+ (let [[xa ya] (first (rsubseq results <= x))
+ [xb yb] (first (subseq results > x))]
+ (if (and xa xb)
+ (/ (+ (* ya (- xb x)) (* yb (- x xa)))
+ (- xb xa))
+ (or ya yb))))))
+
+
+;-----
+(def f (interpolate [[0 0] [10 10] [15 5]]))
+;= #'user/f
+(map f [2 10 12])
+;= (2 10 8)
+
+
+;-----
+(get [:a :b :c] 2)
+;= :c
+(get {:a 5 :b 6} :b)
+;= 6
+(get {:a 5 :b 6} :c 7)
+;= 7
+(get #{1 2 3} 3)
+;= 3
+
+
+;-----
+([:a :b :c] 2)
+;= :c
+({:a 5 :b 6} :b)
+;= 6
+({:a 5 :b 6} :c 7)
+;= 7
+(#{1 2 3} 3)
+;= 3
+
+
+;-----
+([:a :b :c] -1)
+;= #<IndexOutOfBoundsException java.lang.IndexOutOfBoundsException>
+
+
+;-----
+(get {:a 5 :b 6} :b)
+;= 6
+(get {:a 5 :b 6} :c 7)
+;= 7
+(get #{:a :b :c} :d)
+;= nil
+
+
+;-----
+(:b {:a 5 :b 6})
+;= 6
+(:c {:a 5 :b 6} 7)
+;= 7
+(:d #{:a :b :c})
+;= nil
+
+
+;-----
+(defn get-foo
+ [map]
+ (:foo map))
+;= #'user/get-foo
+(get-foo nil)
+;= nil
+(defn get-bar
+ [map]
+ (map :bar))
+;= #'user/get-bar
+(get-bar nil)
+;= #<NullPointerException java.lang.NullPointerException>
+
+
+;-----
+(map :name [{:age 21 :name "David"}
+ {:gender :f :name "Suzanne"}
+ {:name "Sara" :location "NYC"}])
+;= ("David" "Suzanne" "Sara")
+
+
+;-----
+(some #{1 3 7} [0 2 4 5 6])
+;= nil
+(some #{1 3 7} [0 2 3 4 5 6])
+;= 3
+
+
+;-----
+(filter :age [{:age 21 :name "David"}
+ {:gender :f :name "Suzanne"}
+ {:name "Sara" :location "NYC"}])
+;= ({:age 21, :name "David"})
+
+(filter (comp (partial <= 25) :age) [{:age 21 :name "David"}
+ {:gender :f :name "Suzanne" :age 20}
+ {:name "Sara" :location "NYC" :age 34}])
+;= ({:age 34, :name "Sara", :location "NYC"})
+
+
+;-----
+(remove #{5 7} (cons false (range 10)))
+;= (false 0 1 2 3 4 6 8 9)
+(remove #{5 7 false} (cons false (range 10)))
+;= (false 0 1 2 3 4 6 8 9)
+
+
+;-----
+(remove (partial contains? #{5 7 false}) (cons false (range 10)))
+;= (0 1 2 3 4 6 8 9)
+
+
+;-----
+'(1 2 3)
+;= (1 2 3)
+
+
+;-----
+'(1 2 (+ 1 2))
+;= (1 2 (+ 1 2))
+
+
+;-----
+(list 1 2 (+ 1 2))
+;= (1 2 3)
+
+
+;-----
+(vector 1 2 3)
+;= [1 2 3]
+(vec (range 5))
+;= [0 1 2 3 4]
+
+
+;-----
+(defn euclidian-division
+ [x y]
+ [(quot x y) (rem x y)])
+
+(euclidian-division 42 8)
+;= [5 2]
+
+
+;-----
+(let [[q r] (euclidian-division 53 7)]
+ (str "53/7 = " q " * 7 + " r))
+;= "53/7 = 7 * 7 + 4"
+
+
+;-----
+(def point-3d [42 26 -7])
+
+(def travel-legs [["LYS" "FRA"] ["FRA" "PHL"] ["PHL" "RDU"]])
+
+
+;-----
+#{1 2 3}
+;= #{1 2 3}
+#{1 2 3 3}
+;= #<IllegalArgumentException java.lang.IllegalArgumentException:
+;= Duplicate key: 3>
+
+
+;-----
+(hash-set :a :b :c :d)
+;= #{:a :c :b :d}
+
+
+;-----
+(set [1 6 1 8 3 7 7])
+;= #{1 3 6 7 8}
+
+
+;-----
+(apply str (remove (set "aeiouy") "vowels are useless"))
+;= "vwls r slss"
+
+(defn numeric? [s] (every? (set "0123456789") s))
+;= #'user/numeric?
+(numeric? "123")
+;= true
+(numeric? "42b")
+;= false
+
+
+;-----
+{:a 5 :b 6}
+;= {:a 5, :b 6}
+{:a 5 :a 5}
+;= #<IllegalArgumentException java.lang.IllegalArgumentException:
+;= Duplicate key: :a>
+
+
+;-----
+(hash-map :a 5 :b 6)
+;= {:a 5, :b 6}
+(apply hash-map [:a 5 :b 6])
+;= {:a 5, :b 6}
+
+
+;-----
+(keys m)
+;= (:a :b :c)
+(vals m)
+;= (1 2 3)
+
+
+;-----
+(map key m)
+;= (:a :c :b)
+(map val m)
+;= (1 3 2)
+
+
+;-----
+(def playlist
+ [{:title "Elephant", :artist "The White Stripes", :year 2003}
+ {:title "Helioself", :artist "Papas Fritas", :year 1997}
+ {:title "Stories from the City, Stories from the Sea",
+ :artist "PJ Harvey", :year 2000}
+ {:title "Buildings and Grounds", :artist "Papas Fritas", :year 2000}
+ {:title "Zen Rodeo", :artist "Mardi Gras BB", :year 2002}])
+
+
+;-----
+(map :title playlist)
+;= ("Elephant" "Helioself" "Stories from the City, Stories from the Sea"
+;= "Buildings and Grounds" "Zen Rodeo")
+
+
+;-----
+(defn summarize [{:keys [title artist year]}]
+ (str title " / " artist " / " year))
+
+
+;-----
+(group-by #(rem % 3) (range 10))
+;= {0 [0 3 6 9], 1 [1 4 7], 2 [2 5 8]}
+
+
+;-----
+(group-by :artist playlist)
+;= {"Papas Fritas" [{:title "Helioself", :artist "Papas Fritas", :year 1997}
+;= {:title "Buildings and Grounds", :artist "Papas Fritas"}]
+;= ...}
+
+
+;-----
+(into {} (for [[k v] (group-by key-fn coll)]
+ [k (summarize v)]))
+
+
+;-----
+(defn reduce-by
+ [key-fn f init coll]
+ (reduce (fn [summaries x]
+ (let [k (key-fn x)]
+ (assoc summaries k (f (summaries k init) x))))
+ {} coll))
+
+
+;-----
+(def orders
+ [{:product "Clock", :customer "Wile Coyote", :qty 6, :total 300}
+ {:product "Dynamite", :customer "Wile Coyote", :qty 20, :total 5000}
+ {:product "Shotgun", :customer "Elmer Fudd", :qty 2, :total 800}
+ {:product "Shells", :customer "Elmer Fudd", :qty 4, :total 100}
+ {:product "Hole", :customer "Wile Coyote", :qty 1, :total 1000}
+ {:product "Anvil", :customer "Elmer Fudd", :qty 2, :total 300}
+ {:product "Anvil", :customer "Wile Coyote", :qty 6, :total 900}])
+
+
+;-----
+(reduce-by :customer #(+ %1 (:total %2)) 0 orders)
+;= {"Elmer Fudd" 1200, "Wile Coyote" 7200}
+
+
+;-----
+(reduce-by :product #(conj %1 (:customer %2)) #{} orders)
+;= {"Anvil" #{"Wile Coyote" "Elmer Fudd"},
+;= "Hole" #{"Wile Coyote"},
+;= "Shells" #{"Elmer Fudd"},
+;= "Shotgun" #{"Elmer Fudd"},
+;= "Dynamite" #{"Wile Coyote"},
+;= "Clock" #{"Wile Coyote"}}
+
+
+;-----
+(fn [order]
+ [(:customer order) (:product order)])
+
+#(vector (:customer %) (:product %))
+
+(fn [{:keys [customer product]}]
+ [customer product])
+
+(juxt :customer :product)
+
+
+;-----
+(reduce-by (juxt :customer :product)
+ #(+ %1 (:total %2)) 0 orders)
+;= {["Wile Coyote" "Anvil"] 900,
+;= ["Elmer Fudd" "Anvil"] 300,
+;= ["Wile Coyote" "Hole"] 1000,
+;= ["Elmer Fudd" "Shells"] 100,
+;= ["Elmer Fudd" "Shotgun"] 800,
+;= ["Wile Coyote" "Dynamite"] 5000,
+;= ["Wile Coyote" "Clock"] 300}
+
+
+;-----
+(defn reduce-by-in
+ [keys-fn f init coll]
+ (reduce (fn [summaries x]
+ (let [ks (keys-fn x)]
+ (assoc-in summaries ks
+ (f (get-in summaries ks init) x))))
+ {} coll))
+
+
+;-----
+(reduce-by-in (juxt :customer :product)
+ #(+ %1 (:total %2)) 0 orders)
+;= {"Elmer Fudd" {"Anvil" 300,
+;= "Shells" 100,
+;= "Shotgun" 800},
+;= "Wile Coyote" {"Anvil" 900,
+;= "Hole" 1000,
+;= "Dynamite" 5000,
+;= "Clock" 300}}
+
+
+;-----
+(def flat-breakup
+ {["Wile Coyote" "Anvil"] 900,
+ ["Elmer Fudd" "Anvil"] 300,
+ ["Wile Coyote" "Hole"] 1000,
+ ["Elmer Fudd" "Shells"] 100,
+ ["Elmer Fudd" "Shotgun"] 800,
+ ["Wile Coyote" "Dynamite"] 5000,
+ ["Wile Coyote" "Clock"] 300})
+
+
+;-----
+(reduce #(apply assoc-in %1 %2) {} flat-breakup)
+;= {"Elmer Fudd" {"Shells" 100,
+;= "Anvil" 300,
+;= "Shotgun" 800},
+;= "Wile Coyote" {"Hole" 1000,
+;= "Dynamite" 5000,
+;= "Clock" 300,
+;= "Anvil" 900}}
+
+
+;-----
+(+ 1 2)
+;= 3
+
+
+;-----
+(def v (vec (range 1e6)))
+;= #'user/v
+(count v)
+;= 1000000
+(def v2 (conj v 1e6))
+;= #'user/v2
+(count v2)
+;= 1000001
+(count v)
+;= 1000000
+
+
+;-----
+(def a (list 1 2 3))
+
+
+;-----
+(def b (conj a 0))
+;= #'user/b
+b
+;= (0 1 2 3)
+
+
+;-----
+(def c (rest a))
+;= #'user/c
+c
+;= (2 3)
+
+
+;-----
+(def a {:a 5 :b 6 :c 7 :d 8})
+
+
+;-----
+(def b (assoc a :c 0))
+;= #'user/b
+b
+;= {:a 5, :c 0, :b 6, :d 8}
+
+
+;-----
+(def c (dissoc a :d))
+;= #'user/c
+c
+;= {:a 5, :c 7, :b 6}
+
+
+;-----
+(def version1 {:name "Chas" :info {:age 31}})
+;= #'user/version1
+(def version2 (update-in version1 [:info :age] + 3))
+;= #'user/version2
+version1
+;= {:info {:age 31}, :name "Chas"}
+version2
+;= {:info {:age 34}, :name "Chas"}
+
+
+;-----
+(def x (transient []))
+;= #'user/x
+(def y (conj! x 1))
+;= #'user/y
+(count y)
+;= 1
+(count x)
+;= 1
+
+
+;-----
+(into #{} (range 5))
+;= #{0 1 2 3 4}
+
+
+;-----
+(defn naive-into
+ [coll source]
+ (reduce conj coll source))
+
+(= (into #{} (range 500))
+ (naive-into #{} (range 500)))
+;= true
+
+
+;-----
+(time (do (into #{} (range 1e6))
+ nil))
+; "Elapsed time: 1756.696 msecs"
+(time (do (naive-into #{} (range 1e6))
+ nil))
+; "Elapsed time: 3394.684 msecs"
+
+
+;-----
+(defn faster-into
+ [coll source]
+ (persistent! (reduce conj! (transient coll) source)))
+
+
+;-----
+(time (do (faster-into #{} (range 1e6))
+ nil))
+; "Elapsed time: 1639.156 msecs"
+
+
+;-----
+(defn transient-capable?
+ "Returns true if a transient can be obtained for the given collection.
+ i.e. tests if `(transient coll)` will succeed."
+ [coll]
+ (instance? clojure.lang.IEditableCollection coll))
+
+
+;-----
+(def v [1 2])
+;= #'user/v
+(def tv (transient v))
+;= #'user/tv
+(conj v 3)
+;= [1 2 3]
+
+
+;-----
+(persistent! tv)
+;= [1 2]
+(get tv 0)
+;= #<IllegalAccessError java.lang.IllegalAccessError:
+;= Transient used after persistent! call>
+
+
+;-----
+(nth (transient [1 2]) 1)
+;= 2
+(get (transient {:a 1 :b 2}) :a)
+;= 1
+((transient {:a 1 :b 2}) :a)
+;= 1
+((transient [1 2]) 1)
+;= 2
+(find (transient {:a 1 :b 2}) :a)
+;= #<CompilerException java.lang.ClassCastException:
+;= clojure.lang.PersistentArrayMap$TransientArrayMap
+;= cannot be cast to java.util.Map (NO_SOURCE_FILE:0)>
+
+
+;-----
+(let [tm (transient {})]
+ (doseq [x (range 100)]
+ (assoc! tm x 0))
+ (persistent! tm))
+;= {0 0, 1 0, 2 0, 3 0, 4 0, 5 0, 6 0, 7 0}
+
+
+;-----
+(let [t (transient {})]
+ @(future (get t :a)))
+;= #<IllegalAccessError java.lang.IllegalAccessError:
+;= Transient used by non-owner thread>
+
+
+;-----
+(persistent! (transient [(transient {})]))
+;= [#<TransientArrayMap clojure.lang.PersistentArrayMap$TransientArrayMap@b57b39f>]
+
+
+;-----
+(= (transient [1 2]) (transient [1 2]))
+;= false
+
+
+;-----
+(def a ^{:created (System/currentTimeMillis)}
+ [1 2 3])
+;= #'user/a
+(meta a)
+;= {:created 1322065198169}
+
+
+;-----
+(meta ^:private [1 2 3])
+;= {:private true}
+(meta ^:private ^:dynamic [1 2 3])
+;= {:dynamic true, :private true}
+
+
+;-----
+(def b (with-meta a (assoc (meta a)
+ :modified (System/currentTimeMillis))))
+;= #'user/b
+(meta b)
+;= {:modified 1322065210115, :created 1322065198169}
+(def b (vary-meta a assoc :modified (System/currentTimeMillis)))
+;= #'user/b
+(meta b)
+;= {:modified 1322065229972, :created 1322065198169}
+
+
+;-----
+(= a b)
+;= true
+a
+;= [1 2 3]
+b
+;= [1 2 3]
+(= ^{:a 5} 'any-value
+ ^{:b 5} 'any-value)
+;= true
+
+
+;-----
+(meta (conj a 500))
+;= {:created 1319481540825}
+
+
+;-----
+(defn empty-board
+ "Creates a rectangular empty board of the specified width
+ and height."
+ [w h]
+ (vec (repeat w (vec (repeat h nil)))))
+
+
+;-----
+(defn populate
+ "Turns :on each of the cells specified as [y, x] coordinates."
+ [board living-cells]
+ (reduce (fn [board coordinates]
+ (assoc-in board coordinates :on))
+ board
+ living-cells))
+
+(def glider (populate (empty-board 6 6) #{[2 0] [2 1] [2 2] [1 2] [0 1]}))
+
+(pprint glider)
+; [[nil :on nil nil nil nil]
+; [nil nil :on nil nil nil]
+; [:on :on :on nil nil nil]
+; [nil nil nil nil nil nil]
+; [nil nil nil nil nil nil]
+; [nil nil nil nil nil nil]]
+
+
+;-----
+(defn neighbours
+ [[x y]]
+ (for [dx [-1 0 1] dy [-1 0 1] :when (not= 0 dx dy)]
+ [(+ dx x) (+ dy y)]))
+
+(defn count-neighbours
+ [board loc]
+ (count (filter #(get-in board %) (neighbours loc))))
+
+(defn indexed-step
+ "Yields the next state of the board, using indices to determine neighbors,
+ liveness, etc."
+ [board]
+ (let [w (count board)
+ h (count (first board))]
+ (loop [new-board board x 0 y 0]
+ (cond
+ (>= x w) new-board
+ (>= y h) (recur new-board (inc x) 0)
+ :else
+ (let [new-liveness
+ (case (count-neighbours board [x y])
+ 2 (get-in board [x y])
+ 3 :on
+ nil)]
+ (recur (assoc-in new-board [x y] new-liveness) x (inc y)))))))
+
+
+;-----
+(-> (iterate indexed-step glider) (nth 8) pprint)
+; [[nil nil nil nil nil nil]
+; [nil nil nil nil nil nil]
+; [nil nil nil :on nil nil]
+; [nil nil nil nil :on nil]
+; [nil nil :on :on :on nil]
+; [nil nil nil nil nil nil]]
+
+
+;-----
+(defn indexed-step2
+ [board]
+ (let [w (count board)
+ h (count (first board))]
+ (reduce
+ (fn [new-board x]
+ (reduce
+ (fn [new-board y]
+ (let [new-liveness
+ (case (count-neighbours board [x y])
+ 2 (get-in board [x y])
+ 3 :on
+ nil)]
+ (assoc-in new-board [x y] new-liveness)))
+ new-board (range h)))
+ board (range w))))
+
+
+;-----
+(defn indexed-step3
+ [board]
+ (let [w (count board)
+ h (count (first board))]
+ (reduce
+ (fn [new-board [x y]]
+ (let [new-liveness
+ (case (count-neighbours board [x y])
+ 2 (get-in board [x y])
+ 3 :on
+ nil)]
+ (assoc-in new-board [x y] new-liveness)))
+ board (for [x (range h) y (range w)] [x y]))))
+
+
+;-----
+(partition 3 1 (range 5))
+;= ((0 1 2) (1 2 3) (2 3 4))
+
+
+;-----
+(partition 3 1 (concat [nil] (range 5) [nil]))
+;= ((nil 0 1) (0 1 2) (1 2 3) (2 3 4) (3 4 nil))
+
+
+;-----
+(defn window
+ "Returns a lazy sequence of 3-item windows centered around each item of coll."
+ [coll]
+ (partition 3 1 (concat [nil] coll [nil])))
+
+
+;-----
+(defn cell-block
+ "Creates a sequences of 3x3 windows from a triple of 3 sequences."
+ [[left mid right]]
+ (window (map vector
+ (or left (repeat nil)) mid (or right (repeat nil)))))
+
+
+;-----
+(defn window
+ "Returns a lazy sequence of 3-item windows centered
+ around each item of coll, padded as necessary with
+ pad or nil."
+ ([coll] (window nil coll))
+ ([pad coll]
+ (partition 3 1 (concat [pad] coll [pad]))))
+
+(defn cell-block
+ "Creates a sequences of 3x3 windows from a triple of 3 sequences."
+ [[left mid right]]
+ (window (map vector left mid right)))
+
+
+;-----
+(defn</