├── .gitignore ├── LICENSE ├── README.markdown ├── clojurebreaker ├── Procfile ├── README.md ├── project.clj ├── resources │ └── public │ │ └── css │ │ └── reset.css ├── scoring-table ├── snippets.clj ├── src │ └── clojurebreaker │ │ ├── game.clj │ │ ├── models │ │ └── game.clj │ │ ├── server.clj │ │ └── views │ │ ├── common.clj │ │ └── welcome.clj └── test │ └── clojurebreaker │ └── game_test.clj ├── data ├── sequences │ └── compositions.xml └── snippets │ ├── Person.java │ ├── StringUtils.java │ ├── bootstrap-mysql.clj │ ├── example-build.xml │ ├── isBlank.java │ ├── macros.clj │ └── xml_callback.clj ├── output ├── .gitignore └── f-1000000 ├── project.clj ├── public ├── 404.html ├── javascripts │ ├── clojure.js │ └── code-highlighter.js └── stylesheets │ └── code-highlighter.css ├── src ├── examples │ ├── atom_snake.clj │ ├── chat.clj │ ├── concurrency.clj │ ├── cryptovault.clj │ ├── cryptovault_complete.clj │ ├── error_kit.clj │ ├── expectorate.clj │ ├── exploring.clj │ ├── functional.clj │ ├── generator.clj │ ├── gulp.clj │ ├── import_static.clj │ ├── index_of_any.clj │ ├── interop.clj │ ├── introduction.clj │ ├── io.clj │ ├── lazy_index_of_any.clj │ ├── life_without_multi.clj │ ├── macros.clj │ ├── macros │ │ ├── bench_1.clj │ │ ├── chain_1.clj │ │ ├── chain_2.clj │ │ ├── chain_3.clj │ │ ├── chain_4.clj │ │ └── chain_5.clj │ ├── male_female.clj │ ├── male_female_seq.clj │ ├── memoized_male_female.clj │ ├── midi.clj │ ├── multimethods.clj │ ├── multimethods │ │ ├── account.clj │ │ ├── default.clj │ │ ├── service_charge_1.clj │ │ ├── service_charge_2.clj │ │ └── service_charge_3.clj │ ├── pi.clj │ ├── preface.clj │ ├── primes.clj │ ├── protocols.clj │ ├── replace_symbol.clj │ ├── sequences.clj │ ├── server │ │ ├── complete.clj │ │ ├── step_1.clj │ │ ├── step_2.clj │ │ └── step_3.clj │ ├── snake.clj │ ├── snippet.clj │ ├── tasklist.clj │ ├── test.clj │ ├── trampoline.clj │ ├── utils.clj │ └── wallingford.clj └── reader │ ├── snake.clj │ ├── snippet_server.clj │ └── tasklist.clj └── test └── examples └── test ├── chat.clj ├── concurrency.clj ├── exploring.clj ├── fail.clj ├── functional.clj ├── index_of_any.clj ├── interop.clj ├── introduction.clj ├── lazy_index_of_any.clj ├── life_without_multi.clj ├── macros.clj ├── macros ├── bench_1.clj ├── chain_1.clj ├── chain_2.clj ├── chain_3.clj ├── chain_4.clj └── chain_5.clj ├── male_female.clj ├── male_female_seq.clj ├── memoized_male_female.clj ├── multimethods.clj ├── multimethods ├── account.clj ├── default.clj ├── service_charge_1.clj ├── service_charge_2.clj └── service_charge_3.clj ├── preface.clj ├── replace_symbol.clj ├── sequences.clj ├── snake.clj ├── snippet.clj ├── tasklist.clj ├── trampoline.clj └── wallingford.clj /.gitignore: -------------------------------------------------------------------------------- 1 | /Programming-Clojure.iml 2 | /build.clj 3 | /hello.out 4 | /snippet-db.lck 5 | /snippet-db.log 6 | /snippet-db.properties 7 | /snippet-db.script 8 | /tmp.clj 9 | lib/* 10 | .lein-failures 11 | classes/* 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | # Sample Code for Programming Clojure 2 | 3 | http://www.pragprog.com/titles/shcloj/programming-clojure 4 | 5 | Copyright 2008-2010 Stuart Halloway. All rights reserved. 6 | 7 | # Getting Started 8 | 9 | This (master) branch of the repository has all the files as referenced 10 | from the book Programming Clojure. All the necessary libraries are 11 | already installed. You should be able to start a REPL with: 12 | 13 | * `bin/repl.sh` (Unix, Mac) 14 | * `bin\repl.bat` (Windows) 15 | 16 | # Want more Clojure Practice? 17 | 18 | [Labrepl](http://github.com/relevance/labrepl) is a free, open-source environment 19 | for exploring the Clojure language. It includes: 20 | 21 | * a web application that presents a set of lab exercises with 22 | step-by-step instructions 23 | * an interactive repl for working with the lab exercises 24 | * solutions with passing tests 25 | * up-to-date versions of Clojure, contrib, incanter, compojure and other libraries to explore 26 | 27 | # Want Training? 28 | 29 | Rich Hickey, the creator of Clojure, and Stuart Halloway, the author 30 | of Programming Clojure, provide Clojure training through the 31 | [Pragmatic Studio](http://pragmaticstudio.com/clojure). 32 | 33 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # Sample Code for Programming Clojure 2 | 3 | http://www.pragprog.com/titles/shcloj2/programming-clojure 4 | Copyright 2011 Stuart Halloway and Aaron Bedra. All rights reserved. 5 | 6 | # Important Notice 7 | 8 | If you are reading the first edition of the book sure you grab the 9 | first-edition branch of this project instead, from 10 | 11 | https://github.com/stuarthalloway/programming-clojure/tree/first-edition 12 | 13 | The first-edition branch has all the files exactly where the book says they 14 | will be. 15 | 16 | # Getting Started 17 | 18 | * Make sure you have Java installed. 19 | * Make sure you have [leiningen](http://github.com/technomancy/leiningen) installed. 20 | * Run `lein deps` to install all the dependent libraries. 21 | * Run `script/repl` to launch a repl. 22 | 23 | # Want more Clojure Practice? 24 | 25 | [Labrepl](http://github.com/relevance/labrepl) is a free, open-source environment 26 | for exploring the Clojure language. It includes: 27 | 28 | * a web application that presents a set of lab exercises with 29 | step-by-step instructions 30 | * an interactive repl for working with the lab exercises 31 | * solutions with passing tests 32 | * up-to-date versions of Clojure, contrib, incanter, compojure and other libraries to explore 33 | -------------------------------------------------------------------------------- /clojurebreaker/Procfile: -------------------------------------------------------------------------------- 1 | web: lein run 2 | -------------------------------------------------------------------------------- /clojurebreaker/README.md: -------------------------------------------------------------------------------- 1 | # clojurebreaker 2 | 3 | A website written in noir. 4 | 5 | ## Usage 6 | 7 | ```bash 8 | lein deps 9 | lein run 10 | ``` 11 | 12 | ## License 13 | 14 | Copyright (C) 2011 FIXME 15 | 16 | Distributed under the Eclipse Public License, the same as Clojure. 17 | 18 | -------------------------------------------------------------------------------- /clojurebreaker/project.clj: -------------------------------------------------------------------------------- 1 | ; START: clojurebreaker-project 2 | (defproject clojurebreaker "0.1.0-SNAPSHOT" 3 | :description "Clojurebreaker game for Programming Clojure 2nd Edition" 4 | :dependencies [[org.clojure/clojure "1.3.0"] 5 | [org.clojure/math.combinatorics "0.0.1"] 6 | [org.clojure/test.generative "0.1.3"] 7 | [noir "1.2.0"]] 8 | :main clojurebreaker.server) 9 | ; END: clojurebreaker-project 10 | -------------------------------------------------------------------------------- /clojurebreaker/resources/public/css/reset.css: -------------------------------------------------------------------------------- 1 | html { 2 | margin:0; 3 | padding:0; 4 | border:0; 5 | } 6 | 7 | body, div, span, object, iframe, 8 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 9 | a, abbr, acronym, address, code, 10 | del, dfn, em, img, q, dl, dt, dd, ol, ul, li, 11 | fieldset, form, label, legend, 12 | table, caption, tbody, tfoot, thead, tr, th, td, 13 | article, aside, dialog, figure, footer, header, 14 | hgroup, nav, section { 15 | margin: 0; 16 | padding: 0; 17 | border: 0; 18 | font-weight: inherit; 19 | font-style: inherit; 20 | font-size: 100%; 21 | font-family: inherit; 22 | vertical-align: baseline; 23 | } 24 | 25 | article, aside, dialog, figure, footer, header, 26 | hgroup, nav, section { 27 | display:block; 28 | } 29 | 30 | body { 31 | line-height: 1.5; 32 | background: white; 33 | } 34 | 35 | table { 36 | border-collapse: separate; 37 | border-spacing: 0; 38 | } 39 | 40 | caption, th, td { 41 | text-align: left; 42 | font-weight: normal; 43 | float:none !important; 44 | } 45 | table, th, td { 46 | vertical-align: middle; 47 | } 48 | 49 | blockquote:before, blockquote:after, q:before, q:after { content: ''; } 50 | blockquote, q { quotes: "" ""; } 51 | 52 | a img { border: none; } 53 | 54 | /*:focus { outline: 0; }*/ 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /clojurebreaker/snippets.clj: -------------------------------------------------------------------------------- 1 | ;; this file contains the intermediate steps in building the 2 | ;; Clojurebreaker game. See the clojurebreaker/src directory for the 3 | ;; completed code. 4 | 5 | ; START:exact-matches-shell 6 | (defn exact-matches 7 | "Given two collections, return the number of positions where 8 | the collections contain equal items." 9 | [c1 c2]) 10 | ; END:exact-matches-shell 11 | 12 | ; START:integers-closed 13 | (defspec closed-under-addition 14 | +' 15 | [^long a ^long b] 16 | (assert (integer? %))) 17 | ; END:integers-closed 18 | 19 | ; START:incorrect-spec 20 | (defspec incorrect-spec 21 | +' 22 | [^long a ^long b] 23 | (assert (< a %)) 24 | (assert (< b %))) 25 | ; END:incorrect-spec 26 | -------------------------------------------------------------------------------- /clojurebreaker/src/clojurebreaker/game.clj: -------------------------------------------------------------------------------- 1 | (ns clojurebreaker.game 2 | (:use clojure.pprint) 3 | (:require [clojure.data :as data] 4 | [clojure.math.combinatorics :as comb] 5 | [clojure.java.io :as io])) 6 | 7 | ;; START:exact-matches 8 | (defn exact-matches 9 | "Given two collections, return the number of positions where 10 | the collections contain equal items." 11 | [c1 c2] 12 | (let [[_ _ matches] (data/diff c1 c2)] 13 | (count (remove nil? matches)))) 14 | ;; END:exact-matches 15 | 16 | ;; START:unordered-matches 17 | (defn unordered-matches 18 | "Given two collections, return a map where each key is an item 19 | in both collections, and each value is the number of times the 20 | value occurs in the collection with fewest occurrences." 21 | [c1 c2] 22 | (let [f1 (select-keys (frequencies c1) c2) 23 | f2 (select-keys (frequencies c2) c1)] 24 | (merge-with min f1 f2))) 25 | ;; END:unordered-matches 26 | 27 | ;; START:score 28 | (defn score 29 | [c1 c2] 30 | (let [exact (exact-matches c1 c2) 31 | unordered (apply + (vals (unordered-matches c1 c2)))] 32 | {:exact exact :unordered (- unordered exact)})) 33 | ;; END: score 34 | 35 | ;; START: generate-turn-inputs 36 | (defn generate-turn-inputs 37 | "Generate all possible turn inputs for a clojurebreaker game 38 | with colors and n columns" 39 | [colors n] 40 | (-> (comb/selections colors n) 41 | (comb/selections 2))) 42 | ;; END: generate-turn-inputs 43 | 44 | ;; START: score-inputs 45 | (defn score-inputs 46 | "Given a sequence of turn inputs, return a lazy sequence of 47 | maps with :secret, :guess, and :score." 48 | [inputs] 49 | (map 50 | (fn [[secret guess]] 51 | {:secret (seq secret) 52 | :guess (seq guess) 53 | :score (score secret guess)}) 54 | inputs)) 55 | ;; END: score-inputs 56 | 57 | (->> (generate-turn-inputs [:r :g :b] 2) 58 | (score-inputs)) 59 | 60 | ;; step 17 score-table 61 | #_(score-all-games [:R :G :B] 3) 62 | 63 | ;; step 18 could check either the clj or the tabular form of score-all-games 64 | ;; into source control and use it as a regression test 65 | ;; (add clojure.java.io) 66 | (use 'clojure.pprint) 67 | (with-open [w (io/writer "scoring-table")] 68 | (binding [*out* w] 69 | (print-table (->> (generate-turn-inputs [:r :g :b :y] 4) 70 | (score-inputs))))) 71 | -------------------------------------------------------------------------------- /clojurebreaker/src/clojurebreaker/models/game.clj: -------------------------------------------------------------------------------- 1 | (ns clojurebreaker.models.game 2 | (:require [clojure.data :as data])) 3 | 4 | (defn create [] 5 | (vec (repeatedly 4 (fn [] (rand-nth ["r" "g" "b" "y"]))))) 6 | 7 | (defn exact-matches 8 | "Given two collections, return the number of positions where 9 | the collections contain equal items." 10 | [c1 c2] 11 | (let [[_ _ matches] (data/diff c1 c2)] 12 | (count (remove nil? matches)))) 13 | 14 | (defn unordered-matches 15 | "Given two collections, return a map where each key is an item 16 | in both collections, and each value is the number of times the 17 | value occurs in the collection with fewest occurrences." 18 | [c1 c2] 19 | (let [f1 (select-keys (frequencies c1) c2) 20 | f2 (select-keys (frequencies c2) c1)] 21 | (merge-with min f1 f2))) 22 | 23 | (defn score 24 | [c1 c2] 25 | (let [exact (exact-matches c1 c2) 26 | unordered (apply + (vals (unordered-matches c1 c2)))] 27 | {:exact exact :unordered (- unordered exact)})) -------------------------------------------------------------------------------- /clojurebreaker/src/clojurebreaker/server.clj: -------------------------------------------------------------------------------- 1 | (ns clojurebreaker.server 2 | (:require [noir.server :as server])) 3 | 4 | (server/load-views "src/clojurebreaker/views/") 5 | 6 | (defn -main [& m] 7 | (let [mode (keyword (or (first m) :dev)) 8 | port (Integer. (get (System/getenv) "PORT" "8080"))] 9 | (server/start port {:mode mode 10 | :ns 'clojurebreaker}))) 11 | 12 | -------------------------------------------------------------------------------- /clojurebreaker/src/clojurebreaker/views/common.clj: -------------------------------------------------------------------------------- 1 | (ns clojurebreaker.views.common 2 | (:use noir.core 3 | hiccup.core 4 | hiccup.page-helpers)) 5 | 6 | (defpartial layout [& content] 7 | (html5 8 | [:head 9 | [:title "Clojurebreaker"] 10 | (include-css "/css/reset.css")] 11 | [:body 12 | [:div#wrapper 13 | content]])) 14 | -------------------------------------------------------------------------------- /clojurebreaker/src/clojurebreaker/views/welcome.clj: -------------------------------------------------------------------------------- 1 | (ns clojurebreaker.views.welcome 2 | (:require [noir.session :as session] 3 | [clojurebreaker.views.common :as common] 4 | [clojurebreaker.models.game :as game]) 5 | (:use [noir.core :only (defpartial defpage render)] 6 | [hiccup.form-helpers :only (form-to text-field submit-button)])) 7 | 8 | ; START: clojurebreaker-partial 9 | (defpartial board [{:keys [one two three four exact unordered]}] 10 | (when (and exact unordered) 11 | [:div "Exact: " exact " Unordered: " unordered]) 12 | (form-to [:post "/guess"] 13 | (text-field "one" one) 14 | (text-field "two" two) 15 | (text-field "three" three) 16 | (text-field "four" four) 17 | (submit-button "Guess"))) 18 | ; END: clojurebreaker-partial 19 | 20 | ; START: clojurebreaker-page 21 | (defpage "/" {:as guesses} 22 | (when-not (session/get :game) 23 | (session/put! :game (game/create))) 24 | (common/layout (board (or guesses nil)))) 25 | ; END: clojurebreaker-page 26 | 27 | ; START: clojurebreaker-post 28 | (defpage [:post "/guess"] {:keys [one two three four]} 29 | (let [result (game/score (session/get :game) [one two three four])] 30 | (if (= (:exact result) 4) 31 | (do (session/remove! :game) 32 | (common/layout 33 | [:h2 "Congratulations, you have solved the puzzle!"] 34 | (form-to [:get "/"] 35 | (submit-button "Start A New Game")))) 36 | (do (session/flash-put! result) 37 | (render "/" {:one one 38 | :two two 39 | :three three 40 | :four four 41 | :exact (:exact result) 42 | :unordered (:unordered result)}))))) 43 | ; END: clojurebreaker-post -------------------------------------------------------------------------------- /clojurebreaker/test/clojurebreaker/game_test.clj: -------------------------------------------------------------------------------- 1 | ;; START:invariants 2 | (ns clojurebreaker.game-test 3 | (:use [clojure.test.generative :only (defspec) :as test]) 4 | (:require [clojure.test.generative.generators :as gen] 5 | [clojurebreaker.game :as game] 6 | [clojure.math.combinatorics :as comb])) 7 | 8 | (defn matches 9 | "Given a score, returns total number of exact plus 10 | unordered matches." 11 | [score] 12 | (+ (:exact score) (:unordered score))) 13 | 14 | (defn scoring-is-symmetric 15 | [secret guess score] 16 | (= score (game/score guess secret))) 17 | 18 | (defn scoring-is-bounded-by-number-of-pegs 19 | [secret guess score] 20 | (< 0 (matches score) (count secret))) 21 | (defn reordering-the-guess-does-not-change-matches 22 | [secret guess score] 23 | (= #{(matches score)} 24 | (into #{} (map 25 | #(matches (game/score secret %)) 26 | (comb/permutations guess))))) 27 | ;; END:invariants 28 | 29 | ;; START:random-secret 30 | (defn random-secret 31 | [] 32 | (gen/vec #(gen/one-of :r :g :b :y) 4)) 33 | ;; END:random-secret 34 | 35 | ;; START:score-invariants 36 | (defspec score-invariants 37 | game/score 38 | [^{:tag `random-secret} secret 39 | ^{:tag `random-secret} guess] 40 | (assert (scoring-is-symmetric secret guess %)) 41 | (assert (scoring-is-bounded-by-number-of-pegs secret guess %)) 42 | (assert (reordering-the-guess-does-not-change-matches secret guess %))) 43 | ;; END:score-invariants 44 | -------------------------------------------------------------------------------- /data/sequences/compositions.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | The Art of the Fugue 4 | 5 | 6 | Fantaisie-Impromptu Op. 66 7 | 8 | 9 | Requiem 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /data/snippets/Person.java: -------------------------------------------------------------------------------- 1 | public class Person { 2 | private String firstName; 3 | private String lastName; 4 | 5 | public Person(String firstName, String lastName) { 6 | this.firstName = firstName; 7 | this.lastName = lastName; 8 | } 9 | 10 | public String getFirstName() { 11 | return firstName; 12 | } 13 | 14 | public void setFirstName(String firstName) { 15 | this.firstName = firstName; 16 | } 17 | 18 | public String getLastName() { 19 | return lastName; 20 | } 21 | 22 | public void setLastName(String lastName) { 23 | this.lastName = lastName; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /data/snippets/StringUtils.java: -------------------------------------------------------------------------------- 1 | // From Apache Commons Lang, http://commons.apache.org/lang/ 2 | public static int indexOfAny(String str, char[] searchChars) { 3 | if (isEmpty(str) || ArrayUtils.isEmpty(searchChars)) { 4 | return -1; 5 | } 6 | for (int i = 0; i < str.length(); i++) { 7 | char ch = str.charAt(i); 8 | for (int j = 0; j < searchChars.length; j++) { 9 | if (searchChars[j] == ch) { 10 | return i; 11 | } 12 | } 13 | } 14 | return -1; 15 | } 16 | -------------------------------------------------------------------------------- /data/snippets/bootstrap-mysql.clj: -------------------------------------------------------------------------------- 1 | (defn bootstrap-mysql 2 | ([] 3 | (bootstrap-mysql "file:///Users/stuart/devtools/java/mysql-connector-java-5.0.6/mysql-connector-java-5.0.6-bin.jar")) 4 | ([jar] 5 | (add-classpath jar) 6 | (java.sql.DriverManager/registerDriver (.newInstance (Class/forName "com.mysql.jdbc.Driver"))))) 7 | 8 | -------------------------------------------------------------------------------- /data/snippets/example-build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Build with "ant jar" and then start the REPL with: 6 | "java -cp clojure.jar clojure.main" 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 24 | 25 | 27 | 28 | 29 | 31 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /data/snippets/isBlank.java: -------------------------------------------------------------------------------- 1 | public class StringUtils { 2 | public static boolean isBlank(String str) { 3 | int strLen; 4 | if (str == null || (strLen = str.length()) == 0) { 5 | return true; 6 | } 7 | for (int i = 0; i < strLen; i++) { 8 | if ((Character.isWhitespace(str.charAt(i)) == false)) { 9 | return false; 10 | } 11 | } 12 | return true; 13 | } 14 | } -------------------------------------------------------------------------------- /data/snippets/macros.clj: -------------------------------------------------------------------------------- 1 | ;; NOT for execution. These are excerpts from Clojure 2 | ;; and are subject to the Clojure License, repeated below: 3 | 4 | ; Copyright (c) Rich Hickey. All rights reserved. 5 | ; The use and distribution terms for this software are covered by the 6 | ; Common Public License 1.0 (http://opensource.org/licenses/cpl.php) 7 | ; which can be found in the file CPL.TXT at the root of this distribution. 8 | ; By using this software in any fashion, you are agreeing to be bound by 9 | ; the terms of this license. 10 | ; You must not remove this notice, or any other, from this software. 11 | 12 | ; START: and 13 | (defmacro and 14 | ([] true) 15 | ([x] x) 16 | ([x & rest] 17 | `(let [and# ~x] ;