├── 10 ├── README.md ├── code.clj └── code.rb ├── 11 ├── README.md ├── code.clj └── playsync │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── doc │ └── intro.md │ ├── project.clj │ ├── src │ └── playsync │ │ └── core.clj │ └── test │ └── playsync │ └── core_test.clj ├── 12 ├── README.md ├── code.clj ├── code.java └── phrasebook │ ├── PirateConversation.java │ ├── PiratePhrases.java │ └── pirate_phrases │ ├── Farewells.java │ └── Greetings.java ├── 13 ├── README.md └── code.clj ├── 01 ├── README.md └── clojure-noob │ ├── .DS_Store │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── doc │ └── intro.md │ ├── project.clj │ ├── src │ └── clojure_noob │ │ └── core.clj │ └── test │ └── clojure_noob │ └── core_test.clj ├── 02 ├── README.md ├── jack-handy └── profiles.clj ├── 03 ├── README.md └── code.clj ├── 04 ├── README.md ├── code.clj ├── code.js └── fwpd │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── doc │ └── intro.md │ ├── project.clj │ ├── src │ └── fwpd │ │ └── core.clj │ ├── suspects.csv │ └── test │ └── fwpd │ └── core_test.clj ├── 05 ├── README.md ├── code.clj ├── code.js └── code.rb ├── 06 ├── README.md ├── code.clj └── the-divine-cheese-code │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── doc │ └── intro.md │ ├── project.clj │ ├── src │ └── the_divine_cheese_code │ │ ├── core.clj │ │ └── visualization │ │ └── svg.clj │ └── test │ └── the_divine_cheese_code │ └── core_test.clj ├── 07 ├── README.md └── code.clj ├── 08 ├── README.md └── code.clj ├── 09 ├── README.md └── code.clj └── README.md /01/README.md: -------------------------------------------------------------------------------- 1 | # Chapter 1: Getting Started 2 | 3 | This directory contains the `clojure-noob` project that you create at 4 | http://www.braveclojure.com/getting-started/. 5 | -------------------------------------------------------------------------------- /01/clojure-noob/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/braveclojure/cftbat-code/006fbe19c8ef9a3db66c458991fb613e5824ee7f/01/clojure-noob/.DS_Store -------------------------------------------------------------------------------- /01/clojure-noob/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | -------------------------------------------------------------------------------- /01/clojure-noob/LICENSE: -------------------------------------------------------------------------------- 1 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 2 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 3 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 4 | 5 | 1. DEFINITIONS 6 | 7 | "Contribution" means: 8 | 9 | a) in the case of the initial Contributor, the initial code and 10 | documentation distributed under this Agreement, and 11 | 12 | b) in the case of each subsequent Contributor: 13 | 14 | i) changes to the Program, and 15 | 16 | ii) additions to the Program; 17 | 18 | where such changes and/or additions to the Program originate from and are 19 | distributed by that particular Contributor. A Contribution 'originates' from 20 | a Contributor if it was added to the Program by such Contributor itself or 21 | anyone acting on such Contributor's behalf. Contributions do not include 22 | additions to the Program which: (i) are separate modules of software 23 | distributed in conjunction with the Program under their own license 24 | agreement, and (ii) are not derivative works of the Program. 25 | 26 | "Contributor" means any person or entity that distributes the Program. 27 | 28 | "Licensed Patents" mean patent claims licensable by a Contributor which are 29 | necessarily infringed by the use or sale of its Contribution alone or when 30 | combined with the Program. 31 | 32 | "Program" means the Contributions distributed in accordance with this 33 | Agreement. 34 | 35 | "Recipient" means anyone who receives the Program under this Agreement, 36 | including all Contributors. 37 | 38 | 2. GRANT OF RIGHTS 39 | 40 | a) Subject to the terms of this Agreement, each Contributor hereby grants 41 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 42 | reproduce, prepare derivative works of, publicly display, publicly perform, 43 | distribute and sublicense the Contribution of such Contributor, if any, and 44 | such derivative works, in source code and object code form. 45 | 46 | b) Subject to the terms of this Agreement, each Contributor hereby grants 47 | Recipient a non-exclusive, worldwide, royalty-free patent license under 48 | Licensed Patents to make, use, sell, offer to sell, import and otherwise 49 | transfer the Contribution of such Contributor, if any, in source code and 50 | object code form. This patent license shall apply to the combination of the 51 | Contribution and the Program if, at the time the Contribution is added by the 52 | Contributor, such addition of the Contribution causes such combination to be 53 | covered by the Licensed Patents. The patent license shall not apply to any 54 | other combinations which include the Contribution. No hardware per se is 55 | licensed hereunder. 56 | 57 | c) Recipient understands that although each Contributor grants the licenses 58 | to its Contributions set forth herein, no assurances are provided by any 59 | Contributor that the Program does not infringe the patent or other 60 | intellectual property rights of any other entity. Each Contributor disclaims 61 | any liability to Recipient for claims brought by any other entity based on 62 | infringement of intellectual property rights or otherwise. As a condition to 63 | exercising the rights and licenses granted hereunder, each Recipient hereby 64 | assumes sole responsibility to secure any other intellectual property rights 65 | needed, if any. For example, if a third party patent license is required to 66 | allow Recipient to distribute the Program, it is Recipient's responsibility 67 | to acquire that license before distributing the Program. 68 | 69 | d) Each Contributor represents that to its knowledge it has sufficient 70 | copyright rights in its Contribution, if any, to grant the copyright license 71 | set forth in this Agreement. 72 | 73 | 3. REQUIREMENTS 74 | 75 | A Contributor may choose to distribute the Program in object code form under 76 | its own license agreement, provided that: 77 | 78 | a) it complies with the terms and conditions of this Agreement; and 79 | 80 | b) its license agreement: 81 | 82 | i) effectively disclaims on behalf of all Contributors all warranties and 83 | conditions, express and implied, including warranties or conditions of title 84 | and non-infringement, and implied warranties or conditions of merchantability 85 | and fitness for a particular purpose; 86 | 87 | ii) effectively excludes on behalf of all Contributors all liability for 88 | damages, including direct, indirect, special, incidental and consequential 89 | damages, such as lost profits; 90 | 91 | iii) states that any provisions which differ from this Agreement are offered 92 | by that Contributor alone and not by any other party; and 93 | 94 | iv) states that source code for the Program is available from such 95 | Contributor, and informs licensees how to obtain it in a reasonable manner on 96 | or through a medium customarily used for software exchange. 97 | 98 | When the Program is made available in source code form: 99 | 100 | a) it must be made available under this Agreement; and 101 | 102 | b) a copy of this Agreement must be included with each copy of the Program. 103 | 104 | Contributors may not remove or alter any copyright notices contained within 105 | the Program. 106 | 107 | Each Contributor must identify itself as the originator of its Contribution, 108 | if any, in a manner that reasonably allows subsequent Recipients to identify 109 | the originator of the Contribution. 110 | 111 | 4. COMMERCIAL DISTRIBUTION 112 | 113 | Commercial distributors of software may accept certain responsibilities with 114 | respect to end users, business partners and the like. While this license is 115 | intended to facilitate the commercial use of the Program, the Contributor who 116 | includes the Program in a commercial product offering should do so in a 117 | manner which does not create potential liability for other Contributors. 118 | Therefore, if a Contributor includes the Program in a commercial product 119 | offering, such Contributor ("Commercial Contributor") hereby agrees to defend 120 | and indemnify every other Contributor ("Indemnified Contributor") against any 121 | losses, damages and costs (collectively "Losses") arising from claims, 122 | lawsuits and other legal actions brought by a third party against the 123 | Indemnified Contributor to the extent caused by the acts or omissions of such 124 | Commercial Contributor in connection with its distribution of the Program in 125 | a commercial product offering. The obligations in this section do not apply 126 | to any claims or Losses relating to any actual or alleged intellectual 127 | property infringement. In order to qualify, an Indemnified Contributor must: 128 | a) promptly notify the Commercial Contributor in writing of such claim, and 129 | b) allow the Commercial Contributor tocontrol, and cooperate with the 130 | Commercial Contributor in, the defense and any related settlement 131 | negotiations. The Indemnified Contributor may participate in any such claim 132 | at its own expense. 133 | 134 | For example, a Contributor might include the Program in a commercial product 135 | offering, Product X. That Contributor is then a Commercial Contributor. If 136 | that Commercial Contributor then makes performance claims, or offers 137 | warranties related to Product X, those performance claims and warranties are 138 | such Commercial Contributor's responsibility alone. Under this section, the 139 | Commercial Contributor would have to defend claims against the other 140 | Contributors related to those performance claims and warranties, and if a 141 | court requires any other Contributor to pay any damages as a result, the 142 | Commercial Contributor must pay those damages. 143 | 144 | 5. NO WARRANTY 145 | 146 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON 147 | AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER 148 | EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR 149 | CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A 150 | PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the 151 | appropriateness of using and distributing the Program and assumes all risks 152 | associated with its exercise of rights under this Agreement , including but 153 | not limited to the risks and costs of program errors, compliance with 154 | applicable laws, damage to or loss of data, programs or equipment, and 155 | unavailability or interruption of operations. 156 | 157 | 6. DISCLAIMER OF LIABILITY 158 | 159 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 160 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 161 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION 162 | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 163 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 164 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 165 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY 166 | OF SUCH DAMAGES. 167 | 168 | 7. GENERAL 169 | 170 | If any provision of this Agreement is invalid or unenforceable under 171 | applicable law, it shall not affect the validity or enforceability of the 172 | remainder of the terms of this Agreement, and without further action by the 173 | parties hereto, such provision shall be reformed to the minimum extent 174 | necessary to make such provision valid and enforceable. 175 | 176 | If Recipient institutes patent litigation against any entity (including a 177 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 178 | (excluding combinations of the Program with other software or hardware) 179 | infringes such Recipient's patent(s), then such Recipient's rights granted 180 | under Section 2(b) shall terminate as of the date such litigation is filed. 181 | 182 | All Recipient's rights under this Agreement shall terminate if it fails to 183 | comply with any of the material terms or conditions of this Agreement and 184 | does not cure such failure in a reasonable period of time after becoming 185 | aware of such noncompliance. If all Recipient's rights under this Agreement 186 | terminate, Recipient agrees to cease use and distribution of the Program as 187 | soon as reasonably practicable. However, Recipient's obligations under this 188 | Agreement and any licenses granted by Recipient relating to the Program shall 189 | continue and survive. 190 | 191 | Everyone is permitted to copy and distribute copies of this Agreement, but in 192 | order to avoid inconsistency the Agreement is copyrighted and may only be 193 | modified in the following manner. The Agreement Steward reserves the right to 194 | publish new versions (including revisions) of this Agreement from time to 195 | time. No one other than the Agreement Steward has the right to modify this 196 | Agreement. The Eclipse Foundation is the initial Agreement Steward. The 197 | Eclipse Foundation may assign the responsibility to serve as the Agreement 198 | Steward to a suitable separate entity. Each new version of the Agreement will 199 | be given a distinguishing version number. The Program (including 200 | Contributions) may always be distributed subject to the version of the 201 | Agreement under which it was received. In addition, after a new version of 202 | the Agreement is published, Contributor may elect to distribute the Program 203 | (including its Contributions) under the new version. Except as expressly 204 | stated in Sections 2(a) and 2(b) above, Recipient receives no rights or 205 | licenses to the intellectual property of any Contributor under this 206 | Agreement, whether expressly, by implication, estoppel or otherwise. All 207 | rights in the Program not expressly granted under this Agreement are 208 | reserved. 209 | 210 | This Agreement is governed by the laws of the State of New York and the 211 | intellectual property laws of the United States of America. No party to this 212 | Agreement will bring a legal action under this Agreement more than one year 213 | after the cause of action arose. Each party waives its rights to a jury trial 214 | in any resulting litigation. 215 | -------------------------------------------------------------------------------- /01/clojure-noob/README.md: -------------------------------------------------------------------------------- 1 | # clojure-noob 2 | 3 | FIXME: description 4 | 5 | ## Installation 6 | 7 | Download from http://example.com/FIXME. 8 | 9 | ## Usage 10 | 11 | FIXME: explanation 12 | 13 | $ java -jar clojure-noob-0.1.0-standalone.jar [args] 14 | 15 | ## Options 16 | 17 | FIXME: listing of options this app accepts. 18 | 19 | ## Examples 20 | 21 | ... 22 | 23 | ### Bugs 24 | 25 | ... 26 | 27 | ### Any Other Sections 28 | ### That You Think 29 | ### Might be Useful 30 | 31 | ## License 32 | 33 | Copyright © 2014 FIXME 34 | 35 | Distributed under the Eclipse Public License either version 1.0 or (at 36 | your option) any later version. 37 | -------------------------------------------------------------------------------- /01/clojure-noob/doc/intro.md: -------------------------------------------------------------------------------- 1 | # Introduction to clojure-noob 2 | 3 | TODO: write [great documentation](http://jacobian.org/writing/what-to-write/) 4 | -------------------------------------------------------------------------------- /01/clojure-noob/project.clj: -------------------------------------------------------------------------------- 1 | (defproject clojure-noob "0.1.0-SNAPSHOT" 2 | :description "FIXME: write description" 3 | :url "http://example.com/FIXME" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :dependencies [[org.clojure/clojure "1.7.0"]] 7 | :main ^:skip-aot clojure-noob.core 8 | :target-path "target/%s" 9 | :profiles {:uberjar {:aot :all}}) 10 | -------------------------------------------------------------------------------- /01/clojure-noob/src/clojure_noob/core.clj: -------------------------------------------------------------------------------- 1 | (ns clojure-noob.core 2 | (:gen-class)) 3 | 4 | (defn -main 5 | "I don't do a whole lot ... yet." 6 | [& args] 7 | (println "I'm a little teapot!")) 8 | -------------------------------------------------------------------------------- /01/clojure-noob/test/clojure_noob/core_test.clj: -------------------------------------------------------------------------------- 1 | (ns clojure-noob.core-test 2 | (:require [clojure.test :refer :all] 3 | [clojure-noob.core :refer :all])) 4 | 5 | (deftest a-test 6 | (testing "FIXME, I fail." 7 | (is (= 0 1)))) 8 | -------------------------------------------------------------------------------- /02/README.md: -------------------------------------------------------------------------------- 1 | # Chapter 2: How to Use Emacs, an Excellent Clojure Editor 2 | 3 | You can find the Clojure-friendly Emacs repo at 4 | https://github.com/flyingmachine/emacs-for-clojure. 5 | 6 | [`profiles.clj`](profiles.clj) contains the code you should put in 7 | `~/.lein/profiles.clj` so that you can easily use emacs with Leiningen 8 | projects. 9 | 10 | [`jack-handy`](jack-handy) has the Jack Handy quotes for you to 11 | practice Emacs key bindings. 12 | -------------------------------------------------------------------------------- /02/jack-handy: -------------------------------------------------------------------------------- 1 | If you were a pirate, you know what would be the one thing that would 2 | really make you mad? Treasure chests with no handles. How the hell are 3 | you supposed to carry it?! 4 | 5 | The face of a child can say it all, especially the mouth part of the 6 | face. 7 | 8 | To me, boxing is like a ballet, except there's no music, no 9 | choreography, and the dancers hit each other. 10 | -------------------------------------------------------------------------------- /02/profiles.clj: -------------------------------------------------------------------------------- 1 | {:user {:plugins [[cider/cider-nrepl "0.8.1"]]}} 2 | -------------------------------------------------------------------------------- /03/README.md: -------------------------------------------------------------------------------- 1 | # Chapter 3: Do Things: A Clojure Crash Course 2 | 3 | [`code.clj`](code.clj) has all the code from the chapter. 4 | -------------------------------------------------------------------------------- /03/code.clj: -------------------------------------------------------------------------------- 1 | ;; valid forms 2 | 1 3 | "a string" 4 | ["a" "vector" "of" "strings"] 5 | 6 | 7 | ;; general form of operations. This won't actually run because 8 | ;; operator, operand1, etc aren't bound 9 | (operator operand1 operand2 ... operandn) 10 | 11 | 12 | ;; Example operations 13 | (+ 1 2 3) 14 | ; => 6 15 | 16 | (str "It was the panda " "in the library " "with a dust buster") 17 | ; => "It was the panda in the library with a dust buster" 18 | 19 | 20 | ;; This is an invalid form. I've commented it out so it doesn't mess 21 | ;; things up if you try to evaluate this file. 22 | ;; (+ 23 | 24 | 25 | ;; General structure of `if` 26 | (if boolean-form 27 | then-form 28 | optional-else-form) 29 | 30 | ;; if examples 31 | (if true 32 | "By Zeus's hammer!" 33 | "By Aquaman's trident!") 34 | ; => "By Zeus's hammer!" 35 | 36 | (if false 37 | "By Zeus's hammer!" 38 | "By Aquaman's trident!") 39 | ; => "By Aquaman's trident!" 40 | 41 | 42 | (if false 43 | "By Odin's Elbow!") 44 | ; => nil 45 | 46 | (if true 47 | (do (println "Success!") 48 | "By Zeus's hammer!") 49 | (do (println "Failure!") 50 | "By Aquaman's trident!")) 51 | ; => Success! 52 | ; => "By Zeus's hammer!" 53 | 54 | 55 | ;; More boolean operators 56 | (when true 57 | (println "Success!") 58 | "abra cadabra") 59 | ; => Success! 60 | ; => "abra cadabra" 61 | 62 | 63 | (nil? 1) 64 | ; => false 65 | 66 | (nil? nil) 67 | ; => true 68 | 69 | (if "bears eat beets" 70 | "bears beets Battlestar Galactica") 71 | ; => "bears beets Battlestar Galactica" 72 | 73 | (if nil 74 | "This won't be the result because nil is falsey" 75 | "nil is falsey") 76 | ; => "nil is falsey" 77 | 78 | (= 1 1) 79 | ; => true 80 | 81 | (= nil nil) 82 | ; => true 83 | 84 | (= 1 2) 85 | ; => false 86 | 87 | (or false nil :large_I_mean_venti :why_cant_I_just_say_large) 88 | ; => :large_I_mean_venti 89 | 90 | (or (= 0 1) (= "yes" "no")) 91 | ; => false 92 | 93 | (or nil) 94 | ; => nil 95 | 96 | (and :free_wifi :hot_coffee) 97 | ; => :hot_coffee 98 | 99 | (and :feelin_super_cool nil false) 100 | ; => nil 101 | 102 | 103 | ;; Naming Values with def 104 | (def failed-protagonist-names 105 | ["Larry Potter" "Doreen the Explorer" "The Incredible Bulk"]) 106 | 107 | failed-protagonist-names 108 | ; => ["Larry Potter" "Doreen the Explorer" "The Incredible Bulk"] 109 | 110 | 111 | ;; This is bad, don't do this 112 | (def severity :mild) 113 | (def error-message "OH GOD! IT'S A DISASTER! WE'RE ") 114 | (if (= severity :mild) 115 | (def error-message (str error-message "MILDLY INCONVENIENCED!")) 116 | (def error-message (str error-message "DOOOOOOOMED!"))) 117 | 118 | ;; This is better 119 | (defn error-message 120 | [severity] 121 | (str "OH GOD! IT'S A DISASTER! WE'RE " 122 | (if (= severity :mild) 123 | "MILDLY INCONVENIENCED!" 124 | "DOOOOOOOMED!"))) 125 | 126 | (error-message :mild) 127 | ; => "OH GOD! IT'S A DISASTER! WE'RE MILDLY INCONVENIENCED!" 128 | 129 | 130 | ;; Oh look, some numbers 131 | 93 132 | 1.2 133 | 1/5 134 | 135 | ;; Oooh and some strings 136 | "Lord Voldemort" 137 | "\"He who must not be named\"" 138 | "\"Great cow of Moscow!\" - Hermes Conrad" 139 | 140 | (def name "Chewbacca") 141 | (str "\"Uggllglglglglglglglll\" - " name) 142 | ; => "Uggllglglglglglglglll" - Chewbacca 143 | 144 | 145 | ;; Maps now 146 | {} 147 | 148 | {:first-name "Charlie" 149 | :last-name "McFishwich"} 150 | 151 | {"string-key" +} 152 | 153 | {:name {:first "John" :middle "Jacob" :last "Jingleheimerschmidt"}} 154 | 155 | (hash-map :a 1 :b 2) 156 | ; => {:a 1 :b 2} 157 | 158 | (get {:a 0 :b 1} :b) 159 | ; => 1 160 | 161 | (get {:a 0 :b {:c "ho hum"}} :b) 162 | ; => {:c "ho hum"} 163 | 164 | 165 | (get {:a 0 :b 1} :c) 166 | ; => nil 167 | 168 | (get {:a 0 :b 1} :c "unicorns?") 169 | ; => "unicorns?" 170 | 171 | 172 | (get-in {:a 0 :b {:c "ho hum"}} [:b :c]) 173 | ; => "ho hum" 174 | 175 | 176 | ({:name "The Human Coffeepot"} :name) 177 | ; => "The Human Coffeepot" 178 | 179 | 180 | ;; Here we're looking at keywords 181 | :a 182 | :rumplestiltsken 183 | :34 184 | :_? 185 | 186 | (:a {:a 1 :b 2 :c 3}) 187 | ; => 1 188 | 189 | (get {:a 1 :b 2 :c 3} :a) 190 | ; => 1 191 | 192 | (:d {:a 1 :b 2 :c 3} "No gnome knows homes like Noah knows") 193 | ; => "No gnome knows homes like Noah knows" 194 | 195 | 196 | ;; Vector time! 197 | [3 2 1] 198 | 199 | (get [3 2 1] 0) 200 | ; => 3 201 | 202 | (get ["a" {:name "Pugsley Winterbottom"} "c"] 1) 203 | ; => {:name "Pugsley Winterbottom"} 204 | 205 | (vector "creepy" "full" "moon") 206 | ; => ["creepy" "full" "moon"] 207 | 208 | (conj [1 2 3] 4) 209 | ; => [1 2 3 4] 210 | 211 | 212 | ;; List time! 213 | '(1 2 3 4) 214 | ; => (1 2 3 4) 215 | 216 | (nth '(:a :b :c) 0) 217 | ; => :a 218 | 219 | (nth '(:a :b :c) 2) 220 | ; => :c 221 | 222 | (list 1 "two" {3 4}) 223 | ; => (1 "two" {3 4}) 224 | 225 | (conj '(1 2 3) 4) 226 | ; => (4 1 2 3) 227 | 228 | 229 | ;; Sets are super cool. Here's some set usage 230 | #{"kurt vonnegut" 20 :icicle} 231 | 232 | (hash-set 1 1 2 2) 233 | ; => #{1 2} 234 | 235 | (conj #{:a :b} :b) 236 | ; => #{:a :b} 237 | 238 | (set [3 3 3 4 4]) 239 | ; => #{3 4} 240 | 241 | (contains? #{:a :b} :a) 242 | ; => true 243 | 244 | (contains? #{:a :b} 3) 245 | ; => false 246 | 247 | (contains? #{nil} nil) 248 | ; => true 249 | 250 | (:a #{:a :b}) 251 | ; => :a 252 | 253 | (get #{:a :b} :a) 254 | ; => :a 255 | 256 | (get #{:a nil} nil) 257 | ; => nil 258 | 259 | (get #{:a :b} "kurt vonnegut") 260 | ; => nil 261 | 262 | 263 | ;; Just some more example function calls 264 | (+ 1 2 3 4) 265 | (* 1 2 3 4) 266 | (first [1 2 3 4]) 267 | 268 | ;; You can return functions as values 269 | (or + -) 270 | ; => # 271 | 272 | ;; Some neat tricks 273 | ((or + -) 1 2 3) 274 | ; => 6 275 | 276 | ((and (= 1 1) +) 1 2 3) 277 | ; => 6 278 | 279 | ((first [+ 0]) 1 2 3) 280 | ; => 6 281 | 282 | 283 | ;; These won't work 284 | (1 2 3 4) 285 | ("test" 1 2 3) 286 | 287 | 288 | ;; Higher-order function examples 289 | (inc 1.1) 290 | ; => 2.1 291 | 292 | (map inc [0 1 2 3]) 293 | ; => (1 2 3 4) 294 | 295 | 296 | ;; Demonstrating recursive evaluation 297 | (+ (inc 199) (/ 100 (- 7 2))) 298 | (+ 200 (/ 100 (- 7 2))) ; evaluated "(inc 199)" 299 | (+ 200 (/ 100 5)) ; evaluated (- 7 2) 300 | (+ 200 20) ; evaluated (/ 100 5) 301 | 220 ; final evaluation 302 | 303 | 304 | ;; Special forms 305 | (if boolean-form 306 | then-form 307 | optional-else-form) 308 | 309 | 310 | (if good-mood 311 | (tweet walking-on-sunshine-lyrics) 312 | (tweet mopey-country-song-lyrics)) 313 | 314 | 315 | ;; Defining your own function 316 | (defn too-enthusiastic 317 | "Return a cheer that might be a bit too enthusiastic" 318 | [name] 319 | (str "OH. MY. GOD! " name " YOU ARE MOST DEFINITELY LIKE THE BEST " 320 | "MAN SLASH WOMAN EVER I LOVE YOU AND WE SHOULD RUN AWAY SOMEWHERE")) 321 | 322 | (too-enthusiastic "Zelda") 323 | ; => "OH. MY. GOD! Zelda YOU ARE MOST DEFINITELY LIKE THE BEST MAN SLASH WOMAN EVER I LOVE YOU AND WE SHOULD RUN AWAY SOMEWHERE" 324 | 325 | 326 | ;; Arity examples 327 | (defn no-params 328 | [] 329 | "I take no parameters!") 330 | (defn one-param 331 | [x] 332 | (str "I take one parameter: " x)) 333 | (defn two-params 334 | [x y] 335 | (str "Two parameters! That's nothing! Pah! I will smoosh them " 336 | "together to spite you! " x y)) 337 | 338 | (defn multi-arity 339 | ;; 3-arity arguments and body 340 | ([first-arg second-arg third-arg] 341 | (do-things first-arg second-arg third-arg)) 342 | ;; 2-arity arguments and body 343 | ([first-arg second-arg] 344 | (do-things first-arg second-arg)) 345 | ;; 1-arity arguments and body 346 | ([first-arg] 347 | (do-things first-arg))) 348 | 349 | 350 | ;; Using arity to provide a default value for an argument 351 | (defn x-chop 352 | "Describe the kind of chop you're inflicting on someone" 353 | ([name chop-type] 354 | (str "I " chop-type " chop " name "! Take that!")) 355 | ([name] 356 | (x-chop name "karate"))) 357 | 358 | (x-chop "Kanye West" "slap") 359 | ; => "I slap chop Kanye West! Take that!" 360 | 361 | (x-chop "Kanye East") 362 | ; => "I karate chop Kanye East! Take that!" 363 | 364 | (defn weird-arity 365 | ([] 366 | "Destiny dressed you this morning, my friend, and now Fear is 367 | trying to pull off your pants. If you give up, if you give in, 368 | you're gonna end up naked with Fear just standing there laughing 369 | at your dangling unmentionables! - the Tick") 370 | ([number] 371 | (inc number))) 372 | 373 | 374 | ;; Rest parameters 375 | (defn codger-communication 376 | [whippersnapper] 377 | (str "Get off my lawn, " whippersnapper "!!!")) 378 | 379 | (defn codger 380 | [& whippersnappers] 381 | (map codger-communication whippersnappers)) 382 | 383 | (codger "Billy" "Anne-Marie" "The Incredible Bulk") 384 | ; => ("Get off my lawn, Billy!!!" 385 | ; => "Get off my lawn, Anne-Marie!!!" 386 | ; => "Get off my lawn, The Incredible Bulk!!!") 387 | 388 | 389 | (defn favorite-things 390 | [name & things] 391 | (str "Hi, " name ", here are my favorite things: " 392 | (clojure.string/join ", " things))) 393 | 394 | (favorite-things "Doreen" "gum" "shoes" "kara-te") 395 | ; => "Hi, Doreen, here are my favorite things: gum, shoes, kara-te" 396 | 397 | 398 | ;; Destructuring 399 | ;; Return the first element of a collection 400 | (defn my-first 401 | [[first-thing]] ; Notice that first-thing is within a vector 402 | first-thing) 403 | 404 | (my-first ["oven" "bike" "war-axe"]) 405 | ; => "oven" 406 | 407 | (defn chooser 408 | [[first-choice second-choice & unimportant-choices]] 409 | (println (str "Your first choice is: " first-choice)) 410 | (println (str "Your second choice is: " second-choice)) 411 | (println (str "We're ignoring the rest of your choices. " 412 | "Here they are in case you need to cry over them: " 413 | (clojure.string/join ", " unimportant-choices)))) 414 | 415 | (chooser ["Marmalade", "Handsome Jack", "Pigpen", "Aquaman"]) 416 | ; => Your first choice is: Marmalade 417 | ; => Your second choice is: Handsome Jack 418 | ; => We're ignoring the rest of your choices. Here they are in case \ 419 | ; => you need to cry over them: Pigpen, Aquaman 420 | 421 | 422 | ;; Destructuring maps 423 | (defn announce-treasure-location 424 | [{lat :lat lng :lng}] 425 | (println (str "Treasure lat: " lat)) 426 | (println (str "Treasure lng: " lng))) 427 | 428 | (announce-treasure-location {:lat 28.22 :lng 81.33}) 429 | ; => Treasure lat: 28.22 430 | ; => Treasure lng: 81.33 431 | 432 | (defn announce-treasure-location 433 | [{:keys [lat lng]}] 434 | (println (str "Treasure lat: " lat)) 435 | (println (str "Treasure lng: " lng))) 436 | 437 | (defn receive-treasure-location 438 | [{:keys [lat lng] :as treasure-location}] 439 | (println (str "Treasure lat: " lat)) 440 | (println (str "Treasure lng: " lng)) 441 | 442 | ;; One would assume that this would put in new coordinates for your ship 443 | (steer-ship! treasure-location)) 444 | 445 | 446 | ;; Function bodies return the last value 447 | (defn illustrative-function 448 | [] 449 | (+ 1 304) 450 | 30 451 | "joe") 452 | 453 | (illustrative-function) 454 | ; => "joe" 455 | 456 | (defn number-comment 457 | [x] 458 | (if (> x 6) 459 | "Oh my gosh! What a big number!" 460 | "That number's OK, I guess")) 461 | 462 | (number-comment 5) 463 | ; => "That number's OK, I guess" 464 | 465 | (number-comment 7) 466 | ; => "Oh my gosh! What a big number!" 467 | 468 | 469 | ;; Anonymous functions 470 | (fn [param-list] 471 | function body) 472 | 473 | (map (fn [name] (str "Hi, " name)) 474 | ["Darth Vader" "Mr. Magoo"]) 475 | ; => ("Hi, Darth Vader" "Hi, Mr. Magoo") 476 | 477 | ((fn [x] (* x 3)) 8) 478 | ; => 24 479 | 480 | 481 | (def my-special-multiplier (fn [x] (* x 3))) 482 | (my-special-multiplier 12) 483 | ; => 36 484 | 485 | 486 | ;; Compact anonymous function 487 | #(* % 3) 488 | 489 | (#(* % 3) 8) 490 | ; => 24 491 | 492 | (map #(str "Hi, " %) 493 | ["Darth Vader" "Mr. Magoo"]) 494 | ; => ("Hi, Darth Vader" "Hi, Mr. Magoo") 495 | 496 | ;; Function call 497 | (* 8 3) 498 | 499 | ;; Anonymous function 500 | #(* % 3) 501 | 502 | 503 | (#(str %1 " and " %2) "cornbread" "butter beans") 504 | ; => "cornbread and butter beans" 505 | 506 | (#(identity %&) 1 "blarg" :yip) 507 | ; => (1 "blarg" :yip) 508 | 509 | 510 | ;; Returning functions 511 | (defn inc-maker 512 | "Create a custom incrementor" 513 | [inc-by] 514 | #(+ % inc-by)) 515 | 516 | (def inc3 (inc-maker 3)) 517 | 518 | (inc3 7) 519 | ; => 10 520 | 521 | 522 | ;; Hobbit violence! 523 | (def asym-hobbit-body-parts [{:name "head" :size 3} 524 | {:name "left-eye" :size 1} 525 | {:name "left-ear" :size 1} 526 | {:name "mouth" :size 1} 527 | {:name "nose" :size 1} 528 | {:name "neck" :size 2} 529 | {:name "left-shoulder" :size 3} 530 | {:name "left-upper-arm" :size 3} 531 | {:name "chest" :size 10} 532 | {:name "back" :size 10} 533 | {:name "left-forearm" :size 3} 534 | {:name "abdomen" :size 6} 535 | {:name "left-kidney" :size 1} 536 | {:name "left-hand" :size 2} 537 | {:name "left-knee" :size 2} 538 | {:name "left-thigh" :size 4} 539 | {:name "left-lower-leg" :size 3} 540 | {:name "left-achilles" :size 1} 541 | {:name "left-foot" :size 2}]) 542 | 543 | 544 | (defn matching-part 545 | [part] 546 | {:name (clojure.string/replace (:name part) #"^left-" "right-") 547 | :size (:size part)}) 548 | 549 | (defn symmetrize-body-parts 550 | "Expects a seq of maps that have a :name and :size" 551 | [asym-body-parts] 552 | (loop [remaining-asym-parts asym-body-parts 553 | final-body-parts []] 554 | (if (empty? remaining-asym-parts) 555 | final-body-parts 556 | (let [[part & remaining] remaining-asym-parts] 557 | (recur remaining 558 | (into final-body-parts 559 | (set [part (matching-part part)]))))))) 560 | 561 | 562 | (symmetrize-body-parts asym-hobbit-body-parts) 563 | ; => [{:name "head", :size 3} 564 | {:name "left-eye", :size 1} 565 | {:name "right-eye", :size 1} 566 | {:name "left-ear", :size 1} 567 | {:name "right-ear", :size 1} 568 | {:name "mouth", :size 1} 569 | {:name "nose", :size 1} 570 | {:name "neck", :size 2} 571 | {:name "left-shoulder", :size 3} 572 | {:name "right-shoulder", :size 3} 573 | {:name "left-upper-arm", :size 3} 574 | {:name "right-upper-arm", :size 3} 575 | {:name "chest", :size 10} 576 | {:name "back", :size 10} 577 | {:name "left-forearm", :size 3} 578 | {:name "right-forearm", :size 3} 579 | {:name "abdomen", :size 6} 580 | {:name "left-kidney", :size 1} 581 | {:name "right-kidney", :size 1} 582 | {:name "left-hand", :size 2} 583 | {:name "right-hand", :size 2} 584 | {:name "left-knee", :size 2} 585 | {:name "right-knee", :size 2} 586 | {:name "left-thigh", :size 4} 587 | {:name "right-thigh", :size 4} 588 | {:name "left-lower-leg", :size 3} 589 | {:name "right-lower-leg", :size 3} 590 | {:name "left-achilles", :size 1} 591 | {:name "right-achilles", :size 1} 592 | {:name "left-foot", :size 2} 593 | {:name "right-foot", :size 2}] 594 | 595 | 596 | ;; Let expressions 597 | (let [x 3] 598 | x) 599 | ; => 3 600 | 601 | (def dalmatian-list 602 | ["Pongo" "Perdita" "Puppy 1" "Puppy 2"]) 603 | (let [dalmatians (take 2 dalmatian-list)] 604 | dalmatians) 605 | ; => ("Pongo" "Perdita") 606 | 607 | 608 | (def x 0) 609 | (let [x 1] x) 610 | ; => 1 611 | 612 | 613 | (def x 0) 614 | (let [x (inc x)] x) 615 | ; => 1 616 | 617 | 618 | (let [[pongo & dalmatians] dalmatian-list] 619 | [pongo dalmatians]) 620 | ; => ["Pongo" ("Perdita" "Puppy 1" "Puppy 2")] 621 | 622 | 623 | (let [[part & remaining] remaining-asym-parts] 624 | (recur remaining 625 | (into final-body-parts 626 | (set [part (matching-part part)])))) 627 | 628 | 629 | ;; Into 630 | (into final-body-parts 631 | (set [part (matching-part part)])) 632 | 633 | 634 | (into [] (set [:a :a])) 635 | ; => [:a] 636 | 637 | 638 | ;; example ugly code 639 | (recur (rest remaining-asym-parts) 640 | (into final-body-parts 641 | (set [(first remaining-asym-parts) (matching-part (first remaining-asym-parts))]))) 642 | 643 | 644 | ;; Loop 645 | (loop [iteration 0] 646 | (println (str "Iteration " iteration)) 647 | (if (> iteration 3) 648 | (println "Goodbye!") 649 | (recur (inc iteration)))) 650 | ; => Iteration 0 651 | ; => Iteration 1 652 | ; => Iteration 2 653 | ; => Iteration 3 654 | ; => Iteration 4 655 | ; => Goodbye! 656 | 657 | 658 | (defn recursive-printer 659 | ([] 660 | (recursive-printer 0)) 661 | ([iteration] 662 | (println iteration) 663 | (if (> iteration 3) 664 | (println "Goodbye!") 665 | (recursive-printer (inc iteration))))) 666 | (recursive-printer) 667 | ; => Iteration 0 668 | ; => Iteration 1 669 | ; => Iteration 2 670 | ; => Iteration 3 671 | ; => Iteration 4 672 | ; => Goodbye! 673 | 674 | 675 | ;; Regular expressions 676 | #"regular-expression" 677 | 678 | (re-find #"^left-" "left-eye") 679 | ; => "left-" 680 | 681 | (re-find #"^left-" "cleft-chin") 682 | ; => nil 683 | 684 | (re-find #"^left-" "wongleblart") 685 | ; => nil 686 | 687 | 688 | ;; More hobbit violence 689 | (defn matching-part 690 | [part] 691 | {:name (clojure.string/replace (:name part) #"^left-" "right-") 692 | :size (:size part)}) 693 | (matching-part {:name "left-eye" :size 1}) 694 | ; => {:name "right-eye" :size 1}] 695 | 696 | (matching-part {:name "head" :size 3}) 697 | ; => {:name "head" :size 3}] 698 | 699 | 700 | ;; sum with reduce 701 | (reduce + [1 2 3 4]) 702 | ; => 10 703 | 704 | (+ (+ (+ 1 2) 3) 4) 705 | 706 | (reduce + 15 [1 2 3 4]) 707 | 708 | 709 | ;; build your own reduce function 710 | (defn my-reduce 711 | ([f initial coll] 712 | (loop [result initial 713 | remaining coll] 714 | (if (empty? remaining) 715 | result 716 | (recur (f result (first remaining)) (rest remaining))))) 717 | ([f [head & tail]] 718 | (my-reduce f head tail))) 719 | 720 | (defn better-symmetrize-body-parts 721 | "Expects a seq of maps that have a :name and :size" 722 | [asym-body-parts] 723 | (reduce (fn [final-body-parts part] 724 | (into final-body-parts (set [part (matching-part part)]))) 725 | [] 726 | asym-body-parts)) 727 | 728 | (defn hit 729 | [asym-body-parts] 730 | (let [sym-parts (better-symmetrize-body-parts asym-body-parts) 731 | body-part-size-sum (reduce + (map :size sym-parts)) 732 | target (rand body-part-size-sum)] 733 | (loop [[part & remaining] sym-parts 734 | accumulated-size (:size part)] 735 | (if (> accumulated-size target) 736 | part 737 | (recur remaining (+ accumulated-size (:size (first remaining)))))))) 738 | 739 | 740 | (hit asym-hobbit-body-parts) 741 | ; => {:name "right-upper-arm", :size 3} 742 | 743 | (hit asym-hobbit-body-parts) 744 | ; => {:name "chest", :size 10} 745 | 746 | (hit asym-hobbit-body-parts) 747 | ; => {:name "left-eye", :size 1} 748 | 749 | 750 | (def dec9 (dec-maker 9)) 751 | (dec9 10) 752 | ; => 1 753 | 754 | (mapset inc [1 1 2 2]) 755 | ; => #{2 3} 756 | -------------------------------------------------------------------------------- /04/README.md: -------------------------------------------------------------------------------- 1 | # Chapter 4: Core Functions in Depth 2 | 3 | [`fwpd`](fwpd) has the application for tracking vampires. 4 | 5 | [`code.js`](code.js) has the JavaScript list manipulation example. 6 | 7 | [`code.clj`](code.clj) has all the Clojure examples from the chapter. 8 | -------------------------------------------------------------------------------- /04/code.clj: -------------------------------------------------------------------------------- 1 | (defn titleize 2 | [topic] 3 | (str topic " for the Brave and True")) 4 | 5 | (map titleize ["Hamsters" "Ragnarok"]) 6 | ; => ("Hamsters for the Brave and True" "Ragnarok for the Brave and True") 7 | 8 | (map titleize '("Empathy" "Decorating")) 9 | ; => ("Empathy for the Brave and True" "Decorating for the Brave and True") 10 | 11 | (map titleize #{"Elbows" "Soap Carving"}) 12 | ; => ("Elbows for the Brave and True" "Soap Carving for the Brave and True") 13 | 14 | (map #(titleize (second %)) {:uncomfortable-thing "Winking"}) 15 | ; => ("Winking for the Brave and True") 16 | 17 | (seq '(1 2 3)) 18 | ; => (1 2 3) 19 | 20 | (seq [1 2 3]) 21 | ; => (1 2 3) 22 | 23 | (seq #{1 2 3}) 24 | ; => (1 2 3) 25 | 26 | (seq {:name "Bill Compton" :occupation "Dead mopey guy"}) 27 | ; => ([:name "Bill Compton"] [:occupation "Dead mopey guy"]) 28 | 29 | (into {} (seq {:a 1 :b 2 :c 3})) 30 | ; => {:a 1, :c 3, :b 2} 31 | 32 | (map inc [1 2 3]) 33 | ; => (2 3 4) 34 | 35 | (map str ["a" "b" "c"] ["A" "B" "C"]) 36 | ; => ("aA" "bB" "cC") 37 | 38 | (list (str "a" "A") (str "b" "B") (str "c" "C")) 39 | 40 | (def human-consumption [8.1 7.3 6.6 5.0]) 41 | (def critter-consumption [0.0 0.2 0.3 1.1]) 42 | (defn unify-diet-data 43 | [human critter] 44 | {:human human 45 | :critter critter}) 46 | 47 | (map unify-diet-data human-consumption critter-consumption) 48 | ; => ({:human 8.1, :critter 0.0} 49 | {:human 7.3, :critter 0.2} 50 | {:human 6.6, :critter 0.3} 51 | {:human 5.0, :critter 1.8} 52 | 53 | 54 | (def sum #(reduce + %)) 55 | (def avg #(/ (sum %) (count %))) 56 | (defn stats 57 | [numbers] 58 | (map #(% numbers) [sum count avg])) 59 | 60 | (stats [3 4 10]) 61 | ; => (17 3 17/3) 62 | 63 | (stats [80 1 44 13 6]) 64 | ; => (144 5 144/5) 65 | 66 | 67 | (def identities 68 | [{:alias "Batman" :real "Bruce Wayne"} 69 | {:alias "Spider-Man" :real "Peter Parker"} 70 | {:alias "Santa" :real "Your mom"} 71 | {:alias "Easter Bunny" :real "Your dad"}]) 72 | 73 | (map :real identities) 74 | ; => ("Bruce Wayne" "Peter Parker" "Your mom" "Your dad") 75 | 76 | (reduce (fn [new-map [key val]] 77 | (assoc new-map key (inc val))) 78 | {} 79 | {:max 30 :min 10}) 80 | ; => {:max 31, :min 11} 81 | 82 | (assoc (assoc {} :max (inc 30)) 83 | :min (inc 10)) 84 | 85 | (reduce (fn [new-map [key val]] 86 | (if (> val 4) 87 | (assoc new-map key val) 88 | new-map)) 89 | {} 90 | {:human 4.1 91 | :critter 3.9}) 92 | ; => {:human 4.1} 93 | 94 | (take 3 [1 2 3 4 5 6 7 8 9 10]) 95 | ; => (1 2 3) 96 | 97 | (drop 3 [1 2 3 4 5 6 7 8 9 10]) 98 | ; => (4 5 6 7 8 9 10) 99 | 100 | (def food-journal 101 | [{:month 1 :day 1 :human 5.3 :critter 2.3} 102 | {:month 1 :day 2 :human 5.1 :critter 2.0} 103 | {:month 2 :day 1 :human 4.9 :critter 2.1} 104 | {:month 2 :day 2 :human 5.0 :critter 2.5} 105 | {:month 3 :day 1 :human 4.2 :critter 3.3} 106 | {:month 3 :day 2 :human 4.0 :critter 3.8} 107 | {:month 4 :day 1 :human 3.7 :critter 3.9} 108 | {:month 4 :day 2 :human 3.7 :critter 3.6}]) 109 | 110 | (take-while #(< (:month %) 3) food-journal) 111 | ; => ({:month 1 :day 1 :human 5.3 :critter 2.3} 112 | {:month 1 :day 2 :human 5.1 :critter 2.0} 113 | {:month 2 :day 1 :human 4.9 :critter 2.1} 114 | {:month 2 :day 2 :human 5.0 :critter 2.5} 115 | 116 | (drop-while #(< (:month %) 3) food-journal) 117 | ; => ({:month 3 :day 1 :human 4.2 :critter 3.3} 118 | {:month 3 :day 2 :human 4.0 :critter 3.8} 119 | {:month 4 :day 1 :human 3.7 :critter 3.9} 120 | {:month 4 :day 2 :human 3.7 :critter 3.6} 121 | 122 | (take-while #(< (:month %) 4) 123 | (drop-while #(< (:month %) 2) food-journal)) 124 | ; => ({:month 2 :day 1 :human 4.9 :critter 2.1} 125 | {:month 2 :day 2 :human 5.0 :critter 2.5} 126 | {:month 3 :day 1 :human 4.2 :critter 3.3} 127 | {:month 3 :day 2 :human 4.0 :critter 3.8} 128 | 129 | (filter #(< (:human %) 5) food-journal) 130 | ; => ({:month 2 :day 1 :human 4.9 :critter 2.1} 131 | {:month 3 :day 1 :human 4.2 :critter 3.3} 132 | {:month 3 :day 2 :human 4.0 :critter 3.8} 133 | {:month 4 :day 1 :human 3.7 :critter 3.9} 134 | {:month 4 :day 2 :human 3.7 :critter 3.6} 135 | 136 | (filter #(< (:month %) 3) food-journal) 137 | ; => ({:month 1 :day 1 :human 5.3 :critter 2.3} 138 | {:month 1 :day 2 :human 5.1 :critter 2.0} 139 | {:month 2 :day 1 :human 4.9 :critter 2.1} 140 | {:month 2 :day 2 :human 5.0 :critter 2.5} 141 | 142 | (some #(> (:critter %) 5) food-journal) 143 | ; => nil 144 | 145 | (some #(> (:critter %) 3) food-journal) 146 | ; => true 147 | 148 | (some #(and (> (:critter %) 3) %) food-journal) 149 | ; => {:month 3 :day 1 :human 4.2 :critter 3.3} 150 | 151 | (sort [3 1 2]) 152 | ; => (1 2 3) 153 | 154 | (sort-by count ["aaa" "c" "bb"]) 155 | ; => ("c" "bb" "aaa") 156 | 157 | (concat [1 2] [3 4]) 158 | ; => (1 2 3 4) 159 | 160 | (def vampire-database 161 | {0 {:makes-blood-puns? false, :has-pulse? true :name "McFishwich"} 162 | 1 {:makes-blood-puns? false, :has-pulse? true :name "McMackson"} 163 | 2 {:makes-blood-puns? true, :has-pulse? false :name "Damon Salvatore"} 164 | 3 {:makes-blood-puns? true, :has-pulse? true :name "Mickey Mouse"}}) 165 | 166 | (defn vampire-related-details 167 | [social-security-number] 168 | (Thread/sleep 1000) 169 | (get vampire-database social-security-number)) 170 | 171 | (defn vampire? 172 | [record] 173 | (and (:makes-blood-puns? record) 174 | (not (:has-pulse? record)) 175 | record)) 176 | 177 | (defn identify-vampire 178 | [social-security-numbers] 179 | (first (filter vampire? 180 | (map vampire-related-details social-security-numbers)))) 181 | 182 | 183 | (time (vampire-related-details 0)) 184 | ; => "Elapsed time: 1001.042 msecs" 185 | ; => {:name "McFishwich", :makes-blood-puns? false, :has-pulse? true} 186 | 187 | (time (def mapped-details (map vampire-related-details (range 0 1000000)))) 188 | ; => "Elapsed time: 0.049 msecs" 189 | ; => #'user/mapped-details 190 | 191 | (time (first mapped-details)) 192 | ; => "Elapsed time: 32030.767 msecs" 193 | ; => {:name "McFishwich", :makes-blood-puns? false, :has-pulse? true} 194 | 195 | (time (first mapped-details)) 196 | ; => "Elapsed time: 0.022 msecs" 197 | ; => {:name "McFishwich", :makes-blood-puns? false, :has-pulse? true} 198 | 199 | (time (identify-vampire (range 0 1000000))) 200 | "Elapsed time: 32019.912 msecs" 201 | ; => {:name "Damon Salvatore", :makes-blood-puns? true, :has-pulse? false} 202 | 203 | (concat (take 8 (repeat "na")) ["Batman!"]) 204 | ; => ("na" "na" "na" "na" "na" "na" "na" "na" "Batman!") 205 | 206 | (take 3 (repeatedly (fn [] (rand-int 10)))) 207 | ; => (1 4 0) 208 | 209 | (defn even-numbers 210 | ([] (even-numbers 0)) 211 | ([n] (cons n (lazy-seq (even-numbers (+ n 2)))))) 212 | 213 | (take 10 (even-numbers)) 214 | ; => (0 2 4 6 8 10 12 14 16 18) 215 | 216 | (cons 0 '(2 4 6)) 217 | ; => (0 2 4 6) 218 | 219 | (empty? []) 220 | ; => true 221 | 222 | (empty? ["no!"]) 223 | ; => false 224 | 225 | (map identity {:sunlight-reaction "Glitter!"}) 226 | ; => ([:sunlight-reaction "Glitter!"]) 227 | 228 | (into {} (map identity {:sunlight-reaction "Glitter!"})) 229 | ; => {:sunlight-reaction "Glitter!"} 230 | 231 | (map identity [:garlic :sesame-oil :fried-eggs]) 232 | ; => (:garlic :sesame-oil :fried-eggs) 233 | 234 | (into [] (map identity [:garlic :sesame-oil :fried-eggs])) 235 | ; => [:garlic :sesame-oil :fried-eggs] 236 | 237 | (map identity [:garlic-clove :garlic-clove]) 238 | ; => (:garlic-clove :garlic-clove) 239 | 240 | (into #{} (map identity [:garlic-clove :garlic-clove])) 241 | ; => #{:garlic-clove} 242 | 243 | (into {:favorite-emotion "gloomy"} [[:sunlight-reaction "Glitter!"]]) 244 | ; => {:favorite-emotion "gloomy" :sunlight-reaction "Glitter!"} 245 | 246 | (into ["cherry"] '("pine" "spruce")) 247 | ; => ["cherry" "pine" "spruce"] 248 | 249 | (into {:favorite-animal "kitty"} {:least-favorite-smell "dog" 250 | :relationship-with-teenager "creepy"}) 251 | ; => {:favorite-animal "kitty" 252 | ; => :relationship-with-teenager "creepy" 253 | ; => :least-favorite-smell "dog"} 254 | 255 | (conj [0] [1]) 256 | ; => [0 [1]] 257 | 258 | (into [0] [1]) 259 | ; => [0 1] 260 | 261 | (conj [0] 1) 262 | ; => [0 1] 263 | 264 | (conj [0] 1 2 3 4) 265 | ; => [0 1 2 3 4] 266 | 267 | (conj {:time "midnight"} [:place "ye olde cemetarium"]) 268 | ; => {:place "ye olde cemetarium" :time "midnight"} 269 | 270 | (defn my-conj 271 | [target & additions] 272 | (into target additions)) 273 | 274 | (my-conj [0] 1 2 3) 275 | ; => [0 1 2 3] 276 | 277 | (max 0 1 2) 278 | ; => 2 279 | 280 | (max [0 1 2]) 281 | ; => [0 1 2] 282 | 283 | (apply max [0 1 2]) 284 | ; => 2 285 | 286 | (defn my-into 287 | [target additions] 288 | (apply conj target additions)) 289 | 290 | (my-into [0] [1 2 3]) 291 | ; => [0 1 2 3] 292 | 293 | (def add10 (partial + 10)) 294 | (add10 3) 295 | ; => 13 296 | (add10 5) 297 | ; => 15 298 | 299 | (def add-missing-elements 300 | (partial conj ["water" "earth" "air"])) 301 | 302 | (add-missing-elements "unobtainium" "adamantium") 303 | ; => ["water" "earth" "air" "unobtainium" "adamantium"] 304 | 305 | 306 | (defn my-partial 307 | [partialized-fn & args] 308 | (fn [& more-args] 309 | (apply partialized-fn (into args more-args)))) 310 | 311 | (def add20 (my-partial + 20)) 312 | (add20 3) 313 | ; => 23 314 | 315 | (fn [& more-args] 316 | (apply + (into [20] more-args))) 317 | 318 | (defn lousy-logger 319 | [log-level message] 320 | (condp = log-level 321 | :warn (clojure.string/lower-case message) 322 | :emergency (clojure.string/upper-case message))) 323 | 324 | (def warn (partial lousy-logger :warn)) 325 | 326 | (warn "Red light ahead") 327 | ; => "red light ahead" 328 | 329 | (defn identify-humans 330 | [social-security-numbers] 331 | (filter #(not (vampire? %)) 332 | (map vampire-related-details social-security-numbers))) 333 | 334 | (def not-vampire? (complement vampire?)) 335 | (defn identify-humans 336 | [social-security-numbers] 337 | (filter not-vampire? 338 | (map vampire-related-details social-security-numbers))) 339 | 340 | (defn my-complement 341 | [fun] 342 | (fn [& args] 343 | (not (apply fun args)))) 344 | 345 | (def my-pos? (complement neg?)) 346 | (my-pos? 1) 347 | ; => true 348 | 349 | (my-pos? -1) 350 | ; => false 351 | 352 | 353 | ;; Examples from FWPD 354 | 355 | (slurp filename) 356 | ; => "Edward Cullen,10\nBella Swan,0\nCharlie Swan,0\nJacob Black,3\nCarlisle Cullen,6" 357 | 358 | (parse (slurp filename)) 359 | ; => (["Edward Cullen" "10"] ["Bella Swan" "0"] ["Charlie Swan" "0"] 360 | ; => ["Jacob Black" "3"] ["Carlisle Cullen" "6"]) 361 | 362 | 363 | (first (mapify (parse (slurp filename)))) 364 | ; => {:glitter-index 10, :name "Edward Cullen"} 365 | 366 | (glitter-filter 3 (mapify (parse (slurp filename)))) 367 | ({:name "Edward Cullen", :glitter-index 10} 368 | {:name "Jacob Black", :glitter-index 3} 369 | {:name "Carlisle Cullen", :glitter-index 6}) 370 | -------------------------------------------------------------------------------- /04/code.js: -------------------------------------------------------------------------------- 1 | var node3 = { 2 | value: "last", 3 | next: null 4 | }; 5 | 6 | var node2 = { 7 | value: "middle", 8 | next: node3 9 | }; 10 | 11 | var node1 = { 12 | value: "first", 13 | next: node2 14 | }; 15 | 16 | var first = function(node) { 17 | return node.value; 18 | }; 19 | 20 | var rest = function(node) { 21 | return node.next; 22 | }; 23 | 24 | var cons = function(newValue, node) { 25 | return { 26 | value: newValue, 27 | next: node 28 | }; 29 | }; 30 | 31 | first(node1); 32 | // => "first" 33 | 34 | first(rest(node1)); 35 | // => "middle" 36 | 37 | first(rest(rest(node1))); 38 | // => "last" 39 | 40 | var node0 = cons("new first", node1); 41 | first(node0); 42 | // => "new first" 43 | 44 | first(rest(node0)); 45 | // => "first" 46 | 47 | 48 | var map = function (list, transform) { 49 | if (list === null) { 50 | return null; 51 | } else { 52 | return cons(transform(first(list)), map(rest(list), transform)); 53 | } 54 | } 55 | 56 | first( 57 | map(node1, function (val) { return val + " mapped!"}) 58 | ); 59 | // => "first mapped!" 60 | 61 | var first = function (array) { 62 | return array[0]; 63 | } 64 | 65 | var rest = function (array) { 66 | var sliced = array.slice(1, array.length); 67 | if (sliced.length == 0) { 68 | return null; 69 | } else { 70 | return sliced; 71 | } 72 | } 73 | 74 | var cons = function (newValue, array) { 75 | return [newValue].concat(array); 76 | } 77 | 78 | var list = ["Transylvania", "Forks, WA"]; 79 | map(list, function (val) { return val + " mapped!"}) 80 | // => ["Transylvania mapped!", "Forks, WA mapped!"] 81 | -------------------------------------------------------------------------------- /04/fwpd/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | .hgignore 11 | .hg/ 12 | -------------------------------------------------------------------------------- /04/fwpd/LICENSE: -------------------------------------------------------------------------------- 1 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 2 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 3 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 4 | 5 | 1. DEFINITIONS 6 | 7 | "Contribution" means: 8 | 9 | a) in the case of the initial Contributor, the initial code and 10 | documentation distributed under this Agreement, and 11 | 12 | b) in the case of each subsequent Contributor: 13 | 14 | i) changes to the Program, and 15 | 16 | ii) additions to the Program; 17 | 18 | where such changes and/or additions to the Program originate from and are 19 | distributed by that particular Contributor. A Contribution 'originates' from 20 | a Contributor if it was added to the Program by such Contributor itself or 21 | anyone acting on such Contributor's behalf. Contributions do not include 22 | additions to the Program which: (i) are separate modules of software 23 | distributed in conjunction with the Program under their own license 24 | agreement, and (ii) are not derivative works of the Program. 25 | 26 | "Contributor" means any person or entity that distributes the Program. 27 | 28 | "Licensed Patents" mean patent claims licensable by a Contributor which are 29 | necessarily infringed by the use or sale of its Contribution alone or when 30 | combined with the Program. 31 | 32 | "Program" means the Contributions distributed in accordance with this 33 | Agreement. 34 | 35 | "Recipient" means anyone who receives the Program under this Agreement, 36 | including all Contributors. 37 | 38 | 2. GRANT OF RIGHTS 39 | 40 | a) Subject to the terms of this Agreement, each Contributor hereby grants 41 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 42 | reproduce, prepare derivative works of, publicly display, publicly perform, 43 | distribute and sublicense the Contribution of such Contributor, if any, and 44 | such derivative works, in source code and object code form. 45 | 46 | b) Subject to the terms of this Agreement, each Contributor hereby grants 47 | Recipient a non-exclusive, worldwide, royalty-free patent license under 48 | Licensed Patents to make, use, sell, offer to sell, import and otherwise 49 | transfer the Contribution of such Contributor, if any, in source code and 50 | object code form. This patent license shall apply to the combination of the 51 | Contribution and the Program if, at the time the Contribution is added by the 52 | Contributor, such addition of the Contribution causes such combination to be 53 | covered by the Licensed Patents. The patent license shall not apply to any 54 | other combinations which include the Contribution. No hardware per se is 55 | licensed hereunder. 56 | 57 | c) Recipient understands that although each Contributor grants the licenses 58 | to its Contributions set forth herein, no assurances are provided by any 59 | Contributor that the Program does not infringe the patent or other 60 | intellectual property rights of any other entity. Each Contributor disclaims 61 | any liability to Recipient for claims brought by any other entity based on 62 | infringement of intellectual property rights or otherwise. As a condition to 63 | exercising the rights and licenses granted hereunder, each Recipient hereby 64 | assumes sole responsibility to secure any other intellectual property rights 65 | needed, if any. For example, if a third party patent license is required to 66 | allow Recipient to distribute the Program, it is Recipient's responsibility 67 | to acquire that license before distributing the Program. 68 | 69 | d) Each Contributor represents that to its knowledge it has sufficient 70 | copyright rights in its Contribution, if any, to grant the copyright license 71 | set forth in this Agreement. 72 | 73 | 3. REQUIREMENTS 74 | 75 | A Contributor may choose to distribute the Program in object code form under 76 | its own license agreement, provided that: 77 | 78 | a) it complies with the terms and conditions of this Agreement; and 79 | 80 | b) its license agreement: 81 | 82 | i) effectively disclaims on behalf of all Contributors all warranties and 83 | conditions, express and implied, including warranties or conditions of title 84 | and non-infringement, and implied warranties or conditions of merchantability 85 | and fitness for a particular purpose; 86 | 87 | ii) effectively excludes on behalf of all Contributors all liability for 88 | damages, including direct, indirect, special, incidental and consequential 89 | damages, such as lost profits; 90 | 91 | iii) states that any provisions which differ from this Agreement are offered 92 | by that Contributor alone and not by any other party; and 93 | 94 | iv) states that source code for the Program is available from such 95 | Contributor, and informs licensees how to obtain it in a reasonable manner on 96 | or through a medium customarily used for software exchange. 97 | 98 | When the Program is made available in source code form: 99 | 100 | a) it must be made available under this Agreement; and 101 | 102 | b) a copy of this Agreement must be included with each copy of the Program. 103 | 104 | Contributors may not remove or alter any copyright notices contained within 105 | the Program. 106 | 107 | Each Contributor must identify itself as the originator of its Contribution, 108 | if any, in a manner that reasonably allows subsequent Recipients to identify 109 | the originator of the Contribution. 110 | 111 | 4. COMMERCIAL DISTRIBUTION 112 | 113 | Commercial distributors of software may accept certain responsibilities with 114 | respect to end users, business partners and the like. While this license is 115 | intended to facilitate the commercial use of the Program, the Contributor who 116 | includes the Program in a commercial product offering should do so in a 117 | manner which does not create potential liability for other Contributors. 118 | Therefore, if a Contributor includes the Program in a commercial product 119 | offering, such Contributor ("Commercial Contributor") hereby agrees to defend 120 | and indemnify every other Contributor ("Indemnified Contributor") against any 121 | losses, damages and costs (collectively "Losses") arising from claims, 122 | lawsuits and other legal actions brought by a third party against the 123 | Indemnified Contributor to the extent caused by the acts or omissions of such 124 | Commercial Contributor in connection with its distribution of the Program in 125 | a commercial product offering. The obligations in this section do not apply 126 | to any claims or Losses relating to any actual or alleged intellectual 127 | property infringement. In order to qualify, an Indemnified Contributor must: 128 | a) promptly notify the Commercial Contributor in writing of such claim, and 129 | b) allow the Commercial Contributor tocontrol, and cooperate with the 130 | Commercial Contributor in, the defense and any related settlement 131 | negotiations. The Indemnified Contributor may participate in any such claim 132 | at its own expense. 133 | 134 | For example, a Contributor might include the Program in a commercial product 135 | offering, Product X. That Contributor is then a Commercial Contributor. If 136 | that Commercial Contributor then makes performance claims, or offers 137 | warranties related to Product X, those performance claims and warranties are 138 | such Commercial Contributor's responsibility alone. Under this section, the 139 | Commercial Contributor would have to defend claims against the other 140 | Contributors related to those performance claims and warranties, and if a 141 | court requires any other Contributor to pay any damages as a result, the 142 | Commercial Contributor must pay those damages. 143 | 144 | 5. NO WARRANTY 145 | 146 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON 147 | AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER 148 | EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR 149 | CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A 150 | PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the 151 | appropriateness of using and distributing the Program and assumes all risks 152 | associated with its exercise of rights under this Agreement , including but 153 | not limited to the risks and costs of program errors, compliance with 154 | applicable laws, damage to or loss of data, programs or equipment, and 155 | unavailability or interruption of operations. 156 | 157 | 6. DISCLAIMER OF LIABILITY 158 | 159 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 160 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 161 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION 162 | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 163 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 164 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 165 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY 166 | OF SUCH DAMAGES. 167 | 168 | 7. GENERAL 169 | 170 | If any provision of this Agreement is invalid or unenforceable under 171 | applicable law, it shall not affect the validity or enforceability of the 172 | remainder of the terms of this Agreement, and without further action by the 173 | parties hereto, such provision shall be reformed to the minimum extent 174 | necessary to make such provision valid and enforceable. 175 | 176 | If Recipient institutes patent litigation against any entity (including a 177 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 178 | (excluding combinations of the Program with other software or hardware) 179 | infringes such Recipient's patent(s), then such Recipient's rights granted 180 | under Section 2(b) shall terminate as of the date such litigation is filed. 181 | 182 | All Recipient's rights under this Agreement shall terminate if it fails to 183 | comply with any of the material terms or conditions of this Agreement and 184 | does not cure such failure in a reasonable period of time after becoming 185 | aware of such noncompliance. If all Recipient's rights under this Agreement 186 | terminate, Recipient agrees to cease use and distribution of the Program as 187 | soon as reasonably practicable. However, Recipient's obligations under this 188 | Agreement and any licenses granted by Recipient relating to the Program shall 189 | continue and survive. 190 | 191 | Everyone is permitted to copy and distribute copies of this Agreement, but in 192 | order to avoid inconsistency the Agreement is copyrighted and may only be 193 | modified in the following manner. The Agreement Steward reserves the right to 194 | publish new versions (including revisions) of this Agreement from time to 195 | time. No one other than the Agreement Steward has the right to modify this 196 | Agreement. The Eclipse Foundation is the initial Agreement Steward. The 197 | Eclipse Foundation may assign the responsibility to serve as the Agreement 198 | Steward to a suitable separate entity. Each new version of the Agreement will 199 | be given a distinguishing version number. The Program (including 200 | Contributions) may always be distributed subject to the version of the 201 | Agreement under which it was received. In addition, after a new version of 202 | the Agreement is published, Contributor may elect to distribute the Program 203 | (including its Contributions) under the new version. Except as expressly 204 | stated in Sections 2(a) and 2(b) above, Recipient receives no rights or 205 | licenses to the intellectual property of any Contributor under this 206 | Agreement, whether expressly, by implication, estoppel or otherwise. All 207 | rights in the Program not expressly granted under this Agreement are 208 | reserved. 209 | 210 | This Agreement is governed by the laws of the State of New York and the 211 | intellectual property laws of the United States of America. No party to this 212 | Agreement will bring a legal action under this Agreement more than one year 213 | after the cause of action arose. Each party waives its rights to a jury trial 214 | in any resulting litigation. 215 | -------------------------------------------------------------------------------- /04/fwpd/README.md: -------------------------------------------------------------------------------- 1 | # fwpd 2 | 3 | FIXME: description 4 | 5 | ## Installation 6 | 7 | Download from http://example.com/FIXME. 8 | 9 | ## Usage 10 | 11 | FIXME: explanation 12 | 13 | $ java -jar fwpd-0.1.0-standalone.jar [args] 14 | 15 | ## Options 16 | 17 | FIXME: listing of options this app accepts. 18 | 19 | ## Examples 20 | 21 | ... 22 | 23 | ### Bugs 24 | 25 | ... 26 | 27 | ### Any Other Sections 28 | ### That You Think 29 | ### Might be Useful 30 | 31 | ## License 32 | 33 | Copyright © 2015 FIXME 34 | 35 | Distributed under the Eclipse Public License either version 1.0 or (at 36 | your option) any later version. 37 | -------------------------------------------------------------------------------- /04/fwpd/doc/intro.md: -------------------------------------------------------------------------------- 1 | # Introduction to fwpd 2 | 3 | TODO: write [great documentation](http://jacobian.org/writing/what-to-write/) 4 | -------------------------------------------------------------------------------- /04/fwpd/project.clj: -------------------------------------------------------------------------------- 1 | (defproject fwpd "0.1.0-SNAPSHOT" 2 | :description "FIXME: write description" 3 | :url "http://example.com/FIXME" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :dependencies [[org.clojure/clojure "1.6.0"]] 7 | :main ^:skip-aot fwpd.core 8 | :target-path "target/%s" 9 | :profiles {:uberjar {:aot :all}}) 10 | -------------------------------------------------------------------------------- /04/fwpd/src/fwpd/core.clj: -------------------------------------------------------------------------------- 1 | (ns fwpd.core) 2 | 3 | (def filename "suspects.csv") 4 | 5 | (def vamp-keys [:name :glitter-index]) 6 | 7 | (defn str->int 8 | [str] 9 | (Integer. str)) 10 | 11 | (def conversions {:name identity 12 | :glitter-index str->int}) 13 | 14 | (defn convert 15 | [vamp-key value] 16 | ((get conversions vamp-key) value)) 17 | 18 | (defn parse 19 | "Convert a CSV into rows of columns" 20 | [string] 21 | (map #(clojure.string/split % #",") 22 | (clojure.string/split string #"\n"))) 23 | 24 | (defn mapify 25 | "Return a seq of maps like {:name \"Edward Cullen\" :glitter-index 10}" 26 | [rows] 27 | (map (fn [unmapped-row] 28 | (reduce (fn [row-map [vamp-key value]] 29 | (assoc row-map vamp-key (convert vamp-key value))) 30 | {} 31 | (map vector vamp-keys unmapped-row))) 32 | rows)) 33 | 34 | (defn glitter-filter 35 | [minimum-glitter records] 36 | (filter #(>= (:glitter-index %) minimum-glitter) records)) 37 | 38 | -------------------------------------------------------------------------------- /04/fwpd/suspects.csv: -------------------------------------------------------------------------------- 1 | Edward Cullen,10 2 | Bella Swan,0 3 | Charlie Swan,0 4 | Jacob Black,3 5 | Carlisle Cullen,6 -------------------------------------------------------------------------------- /04/fwpd/test/fwpd/core_test.clj: -------------------------------------------------------------------------------- 1 | (ns fwpd.core-test 2 | (:require [clojure.test :refer :all] 3 | [fwpd.core :refer :all])) 4 | 5 | (deftest a-test 6 | (testing "FIXME, I fail." 7 | (is (= 0 1)))) 8 | -------------------------------------------------------------------------------- /05/README.md: -------------------------------------------------------------------------------- 1 | # Chapter 5: Functional Programming 2 | 3 | The main project at the end of the chapter, Peg Thing, is here: https://github.com/flyingmachine/pegthing. Examples of using Peg Thing functions are in [`code.clj`](code.clj), along with the rest of the clojure snippets. 4 | 5 | Inferior JavaScript examples are in [`code.js`](code.js). There's some Ruby code in [`code.rb`](code.rb). 6 | -------------------------------------------------------------------------------- /05/code.clj: -------------------------------------------------------------------------------- 1 | (+ 1 2) 2 | ; => 3 3 | 4 | (defn wisdom 5 | [words] 6 | (str words ", Daniel-san")) 7 | 8 | (wisdom "Always bathe on Fridays") 9 | ; => "Always bathe on Fridays, Daniel-san" 10 | 11 | (defn year-end-evaluation 12 | [] 13 | (if (> (rand) 0.5) 14 | "You get a raise!" 15 | "Better luck next year!")) 16 | 17 | (defn analyze-file 18 | [filename] 19 | (analysis (slurp filename))) 20 | 21 | (defn analysis 22 | [text] 23 | (str "Character count: " (count text))) 24 | 25 | (def great-baby-name "Rosanthony") 26 | great-baby-name 27 | ; => "Rosanthony" 28 | 29 | (let [great-baby-name "Bloodthunder"] 30 | great-baby-name) 31 | ; => "Bloodthunder" 32 | 33 | great-baby-name 34 | ; => "Rosanthony" 35 | 36 | (defn sum 37 | ([vals] (sum vals 0)) 38 | ([vals accumulating-total] 39 | (if (empty? vals) 40 | accumulating-total 41 | (sum (rest vals) (+ (first vals) accumulating-total))))) 42 | 43 | (sum [39 5 1]) ; single-arity body calls two-arity body 44 | (sum [39 5 1] 0) 45 | (sum [5 1] 39) 46 | (sum [1] 44) 47 | (sum [] 45) ; base case is reached, so return accumulating-total 48 | ; => 45 49 | 50 | (defn sum 51 | ([vals] 52 | (sum vals 0)) 53 | ([vals accumulating-total] 54 | (if (empty? vals) 55 | accumulating-total 56 | (recur (rest vals) (+ (first vals) accumulating-total))))) 57 | 58 | 59 | (require '[clojure.string :as s]) 60 | (defn clean 61 | [text] 62 | (s/replace (s/trim text) #"lol" "LOL")) 63 | 64 | (clean "My boa constrictor is so sassy lol! ") 65 | ; => "My boa constrictor is so sassy LOL!" 66 | 67 | ((comp inc *) 2 3) 68 | ; => 7 69 | 70 | (def character 71 | {:name "Smooches McCutes" 72 | :attributes {:intelligence 10 73 | :strength 4 74 | :dexterity 5}}) 75 | (def c-int (comp :intelligence :attributes)) 76 | (def c-str (comp :strength :attributes)) 77 | (def c-dex (comp :dexterity :attributes)) 78 | 79 | (c-int character) 80 | ; => 10 81 | 82 | (c-str character) 83 | ; => 4 84 | 85 | (c-dex character) 86 | ; => 5 87 | 88 | 89 | (fn [c] (:strength (:attributes c))) 90 | 91 | (defn spell-slots 92 | [char] 93 | (int (inc (/ (c-int char) 2)))) 94 | 95 | (spell-slots character) 96 | ; => 6 97 | 98 | (def spell-slots-comp (comp int inc #(/ % 2) c-int)) 99 | 100 | (defn two-comp 101 | [f g] 102 | (fn [& args] 103 | (f (apply g args)))) 104 | 105 | (+ 3 (+ 5 8)) 106 | 107 | (+ 3 13) 108 | 109 | 16 110 | 111 | (defn sleepy-identity 112 | "Returns the given value after 1 second" 113 | [x] 114 | (Thread/sleep 1000) 115 | x) 116 | 117 | (sleepy-identity "Mr. Fantastico") 118 | ; => "Mr. Fantastico" after 1 second 119 | 120 | (sleepy-identity "Mr. Fantastico") 121 | ; => "Mr. Fantastico" after 1 second 122 | 123 | 124 | (def memo-sleepy-identity (memoize sleepy-identity)) 125 | (memo-sleepy-identity "Mr. Fantastico") 126 | ; => "Mr. Fantastico" after 1 second 127 | 128 | (memo-sleepy-identity "Mr. Fantastico") 129 | ; => "Mr. Fantastico" immediately 130 | 131 | 132 | ;; Peg Thing examples 133 | 134 | (take 5 tri) 135 | ; => (1 3 6 10 15) 136 | 137 | (triangular? 5) 138 | ; => false 139 | 140 | (triangular? 6) 141 | ; => true 142 | 143 | (row-tri 1) 144 | ; => 1 145 | 146 | (row-tri 2) 147 | ; => 3 148 | 149 | (row-tri 3) 150 | ; => 6 151 | 152 | (row-num 1) 153 | ; => 1 154 | (row-num 5) 155 | ; => 3 156 | 157 | 158 | (connect {} 15 1 2 4) 159 | ; => {1 {:connections {4 2}} 160 | ; => 4 {:connections {1 2}}} 161 | 162 | (assoc-in {} [:cookie :monster :vocals] "Finntroll") 163 | ; => {:cookie {:monster {:vocals "Finntroll"}}} 164 | 165 | (get-in {:cookie {:monster {:vocals "Finntroll"}}} [:cookie :monster]) 166 | ; => {:vocals "Finntroll"} 167 | 168 | (assoc-in {} [1 :connections 4] 2) 169 | ; => {1 {:connections {4 2}}} 170 | 171 | (connect-down-left {} 15 1) 172 | ; => {1 {:connections {4 2} 173 | ; => 4 {:connections {1 2}}}} 174 | 175 | (connect-down-right {} 15 3) 176 | ; => {3 {:connections {10 6}} 177 | ; => 10 {:connections {3 6}}} 178 | 179 | (add-pos {} 15 1) 180 | {1 {:connections {6 3, 4 2}, :pegged true} 181 | 4 {:connections {1 2}} 182 | 6 {:connections {1 3}}} 183 | 184 | (valid-moves my-board 1) ; => {4 2} 185 | (valid-moves my-board 6) ; => {4 5} 186 | (valid-moves my-board 11) ; => {4 7} 187 | (valid-moves my-board 5) ; => {} 188 | (valid-moves my-board 8) ; => {} 189 | 190 | 191 | (valid-move? my-board 8 4) ; => nil 192 | (valid-move? my-board 1 4) ; => 2 193 | 194 | (characters-as-strings "a b") 195 | ; => ("a" "b") 196 | 197 | (characters-as-strings "a cb") 198 | ; => ("a" "c" "b") 199 | -------------------------------------------------------------------------------- /05/code.js: -------------------------------------------------------------------------------- 1 | var haplessObject = { 2 | emotion: "Carefree!" 3 | }; 4 | 5 | var evilMutator = function(object){ 6 | object.emotion = "So emo :'("; 7 | } 8 | 9 | evilMutator(haplessObject); 10 | haplessObject.emotion; 11 | // => "So emo :'(" 12 | 13 | var wrestlers = getAlligatorWrestlers(); 14 | var totalBites = 0; 15 | var l = wrestlers.length; 16 | 17 | for(var i=0; i < l; i++){ 18 | totalBites += wrestlers[i].timesBitten; 19 | } 20 | 21 | var allPatients = getArkhamPatients(); 22 | var analyzedPatients = []; 23 | var l = allPatients.length; 24 | 25 | for(var i=0; i < l; i++){ 26 | if(allPatients[i].analyzed){ 27 | analyzedPatients.push(allPatients[i]); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /05/code.rb: -------------------------------------------------------------------------------- 1 | class GlamourShotCaption 2 | attr_reader :text 3 | def initialize(text) 4 | @text = text 5 | clean! 6 | end 7 | 8 | private 9 | def clean! 10 | text.trim! 11 | text.gsub!(/lol/, "LOL") 12 | end 13 | end 14 | 15 | best = GlamourShotCaption.new("My boa constrictor is so sassy lol! ") 16 | best.text 17 | ; => "My boa constrictor is so sassy LOL!" 18 | -------------------------------------------------------------------------------- /06/README.md: -------------------------------------------------------------------------------- 1 | # Chapter 6: Organizing Your Project: A Librarian's Tale 2 | 3 | [`the-divine-cheese-code`] contains the leiningen project for catching 4 | a cheese thief 5 | 6 | [`code.clj`](code.clj) has Clojure snippets 7 | -------------------------------------------------------------------------------- /06/code.clj: -------------------------------------------------------------------------------- 1 | (ns-name *ns*) 2 | ; => user 3 | 4 | inc 5 | ; => # 6 | 7 | 'inc 8 | ; => inc 9 | 10 | (map inc [1 2]) 11 | ; => (2 3) 12 | 13 | '(map inc [1 2]) 14 | ; => (map inc [1 2]) 15 | 16 | (def great-books ["East of Eden" "The Glass Bead Game"]) 17 | ; => #'user/great-books 18 | 19 | great-books 20 | ; => ["East of Eden" "The Glass Bead Game"] 21 | 22 | (ns-interns *ns*) 23 | ; => {great-books #'user/great-books} 24 | 25 | (get (ns-interns *ns*) 'great-books) 26 | ; => #'user/great-books 27 | 28 | (deref #'user/great-books) 29 | ; => ["East of Eden" "The Glass Bead Game"] 30 | 31 | great-books 32 | ; => ["East of Eden" "The Glass Bead Game"] 33 | 34 | (def great-books ["The Power of Bees" "Journey to Upstairs"]) 35 | great-books 36 | ; => ["The Power of Bees" "Journey to Upstairs"] 37 | 38 | (create-ns 'cheese.taxonomy) 39 | ; => # 40 | 41 | (ns-name (create-ns 'cheese.taxonomy)) 42 | ; => cheese-taxonomy 43 | 44 | (in-ns 'cheese.analysis) 45 | ; => # 46 | 47 | 48 | (in-ns 'cheese.taxonomy) 49 | (def cheddars ["mild" "medium" "strong" "sharp" "extra sharp"]) 50 | (in-ns 'cheese.analysis) 51 | cheddars 52 | ; => Exception: Unable to resolve symbol: cheddars in this context 53 | 54 | cheese.taxonomy/cheddars 55 | ; => ["mild" "medium" "strong" "sharp" "extra sharp"] 56 | 57 | 58 | (in-ns 'cheese.taxonomy) 59 | (def cheddars ["mild" "medium" "strong" "sharp" "extra sharp"]) 60 | (def bries ["Wisconsin" "Somerset" "Brie de Meaux" "Brie de Melun"]) 61 | (in-ns 'cheese.analysis) 62 | (clojure.core/refer 'cheese.taxonomy) 63 | bries 64 | ; => ["Wisconsin" "Somerset" "Brie de Meaux" "Brie de Melun"] 65 | 66 | cheddars 67 | ; => ["mild" "medium" "strong" "sharp" "extra sharp"] 68 | 69 | (clojure.core/get (clojure.core/ns-map clojure.core/*ns*) 'bries) 70 | ; => #'cheese.taxonomy/bries 71 | 72 | (clojure.core/get (clojure.core/ns-map clojure.core/*ns*) 'cheddars) 73 | ; => #'cheese.taxonomy/cheddars 74 | 75 | 76 | (clojure.core/refer 'cheese.taxonomy :only ['bries]) 77 | bries 78 | ; => ["Wisconsin" "Somerset" "Brie de Meaux" "Brie de Melun"] 79 | cheddars 80 | ; => RuntimeException: Unable to resolve symbol: cheddars 81 | 82 | 83 | (clojure.core/refer 'cheese.taxonomy :exclude ['bries]) 84 | bries 85 | ; => RuntimeException: Unable to resolve symbol: bries 86 | cheddars 87 | ; => ["mild" "medium" "strong" "sharp" "extra sharp"] 88 | 89 | 90 | (clojure.core/refer 'cheese.taxonomy :rename {'bries 'yummy-bries}) 91 | bries 92 | ; => RuntimeException: Unable to resolve symbol: bries 93 | yummy-bries 94 | ; => ["Wisconsin" "Somerset" "Brie de Meaux" "Brie de Melun"] 95 | 96 | 97 | (in-ns 'cheese.analysis) 98 | ;; Notice the dash after "defn" 99 | (defn- private-function 100 | "Just an example function that does nothing" 101 | []) 102 | 103 | (in-ns 'cheese.taxonomy) 104 | (clojure.core/refer-clojure) 105 | (cheese.analysis/private-function) 106 | (refer 'cheese.analysis :only ['private-function]) 107 | 108 | (clojure.core/alias 'taxonomy 'cheese.taxonomy) 109 | taxonomy/bries 110 | ; => ["Wisconsin" "Somerset" "Brie de Meaux" "Brie de Melun"] 111 | 112 | 113 | ;; this: 114 | (require '[the-divine-cheese-code.visualization.svg :as svg]) 115 | ;; is the same as this: 116 | (require 'the-divine-cheese-code.visualization.svg) 117 | (alias 'svg 'the-divine-cheese-code.visualization.svg) 118 | 119 | (svg/points heists) 120 | ; => "50.95,6.97 47.37,8.55 43.3,5.37 47.37,8.55 41.9,12.45" 121 | 122 | 123 | (use '[the-divine-cheese-code.visualization.svg :as svg]) 124 | (= svg/points points) 125 | ; => true 126 | 127 | (= svg/latlng->point latlng->point) 128 | ; => true 129 | 130 | (require 'the-divine-cheese-code.visualization.svg) 131 | (use '[the-divine-cheese-code.visualization.svg :as svg :only [points]]) 132 | (refer 'the-divine-cheese-code.visualization.svg :as svg :only ['points]) 133 | (= svg/points points) 134 | ; => true 135 | 136 | ;; We can use the alias to reach latlng->point 137 | svg/latlng->point 138 | ; This doesn't throw an exception 139 | 140 | ;; But we can't use the bare name 141 | latlng->point 142 | ; This does throw an exception! 143 | 144 | 145 | (ns the-divine-cheese-code.visualization.svg 146 | (:require [clojure.string :as s]) 147 | (:refer-clojure :exclude [min max])) 148 | 149 | (defn comparator-over-maps 150 | [comparison-fn ks] 151 | (fn [maps] 152 | (zipmap ks 153 | (map (fn [k] (apply comparison-fn (map k maps))) 154 | ks)))) 155 | 156 | (def min (comparator-over-maps clojure.core/min [:lat :lng])) 157 | (def max (comparator-over-maps clojure.core/max [:lat :lng])) 158 | 159 | 160 | (min [{:a 1 :b 3} {:a 5 :b 0}]) 161 | ; => {:a 1 :b 0} 162 | 163 | 164 | (zipmap [:a :b] [1 2]) 165 | ; => {:a 1 :b 2} 166 | 167 | 168 | (merge-with - {:lat 50 :lng 10} {:lat 5 :lng 5}) 169 | ; => {:lat 45 :lng 5} 170 | -------------------------------------------------------------------------------- /06/the-divine-cheese-code/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | .hgignore 11 | .hg/ 12 | -------------------------------------------------------------------------------- /06/the-divine-cheese-code/LICENSE: -------------------------------------------------------------------------------- 1 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 2 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 3 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 4 | 5 | 1. DEFINITIONS 6 | 7 | "Contribution" means: 8 | 9 | a) in the case of the initial Contributor, the initial code and 10 | documentation distributed under this Agreement, and 11 | 12 | b) in the case of each subsequent Contributor: 13 | 14 | i) changes to the Program, and 15 | 16 | ii) additions to the Program; 17 | 18 | where such changes and/or additions to the Program originate from and are 19 | distributed by that particular Contributor. A Contribution 'originates' from 20 | a Contributor if it was added to the Program by such Contributor itself or 21 | anyone acting on such Contributor's behalf. Contributions do not include 22 | additions to the Program which: (i) are separate modules of software 23 | distributed in conjunction with the Program under their own license 24 | agreement, and (ii) are not derivative works of the Program. 25 | 26 | "Contributor" means any person or entity that distributes the Program. 27 | 28 | "Licensed Patents" mean patent claims licensable by a Contributor which are 29 | necessarily infringed by the use or sale of its Contribution alone or when 30 | combined with the Program. 31 | 32 | "Program" means the Contributions distributed in accordance with this 33 | Agreement. 34 | 35 | "Recipient" means anyone who receives the Program under this Agreement, 36 | including all Contributors. 37 | 38 | 2. GRANT OF RIGHTS 39 | 40 | a) Subject to the terms of this Agreement, each Contributor hereby grants 41 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 42 | reproduce, prepare derivative works of, publicly display, publicly perform, 43 | distribute and sublicense the Contribution of such Contributor, if any, and 44 | such derivative works, in source code and object code form. 45 | 46 | b) Subject to the terms of this Agreement, each Contributor hereby grants 47 | Recipient a non-exclusive, worldwide, royalty-free patent license under 48 | Licensed Patents to make, use, sell, offer to sell, import and otherwise 49 | transfer the Contribution of such Contributor, if any, in source code and 50 | object code form. This patent license shall apply to the combination of the 51 | Contribution and the Program if, at the time the Contribution is added by the 52 | Contributor, such addition of the Contribution causes such combination to be 53 | covered by the Licensed Patents. The patent license shall not apply to any 54 | other combinations which include the Contribution. No hardware per se is 55 | licensed hereunder. 56 | 57 | c) Recipient understands that although each Contributor grants the licenses 58 | to its Contributions set forth herein, no assurances are provided by any 59 | Contributor that the Program does not infringe the patent or other 60 | intellectual property rights of any other entity. Each Contributor disclaims 61 | any liability to Recipient for claims brought by any other entity based on 62 | infringement of intellectual property rights or otherwise. As a condition to 63 | exercising the rights and licenses granted hereunder, each Recipient hereby 64 | assumes sole responsibility to secure any other intellectual property rights 65 | needed, if any. For example, if a third party patent license is required to 66 | allow Recipient to distribute the Program, it is Recipient's responsibility 67 | to acquire that license before distributing the Program. 68 | 69 | d) Each Contributor represents that to its knowledge it has sufficient 70 | copyright rights in its Contribution, if any, to grant the copyright license 71 | set forth in this Agreement. 72 | 73 | 3. REQUIREMENTS 74 | 75 | A Contributor may choose to distribute the Program in object code form under 76 | its own license agreement, provided that: 77 | 78 | a) it complies with the terms and conditions of this Agreement; and 79 | 80 | b) its license agreement: 81 | 82 | i) effectively disclaims on behalf of all Contributors all warranties and 83 | conditions, express and implied, including warranties or conditions of title 84 | and non-infringement, and implied warranties or conditions of merchantability 85 | and fitness for a particular purpose; 86 | 87 | ii) effectively excludes on behalf of all Contributors all liability for 88 | damages, including direct, indirect, special, incidental and consequential 89 | damages, such as lost profits; 90 | 91 | iii) states that any provisions which differ from this Agreement are offered 92 | by that Contributor alone and not by any other party; and 93 | 94 | iv) states that source code for the Program is available from such 95 | Contributor, and informs licensees how to obtain it in a reasonable manner on 96 | or through a medium customarily used for software exchange. 97 | 98 | When the Program is made available in source code form: 99 | 100 | a) it must be made available under this Agreement; and 101 | 102 | b) a copy of this Agreement must be included with each copy of the Program. 103 | 104 | Contributors may not remove or alter any copyright notices contained within 105 | the Program. 106 | 107 | Each Contributor must identify itself as the originator of its Contribution, 108 | if any, in a manner that reasonably allows subsequent Recipients to identify 109 | the originator of the Contribution. 110 | 111 | 4. COMMERCIAL DISTRIBUTION 112 | 113 | Commercial distributors of software may accept certain responsibilities with 114 | respect to end users, business partners and the like. While this license is 115 | intended to facilitate the commercial use of the Program, the Contributor who 116 | includes the Program in a commercial product offering should do so in a 117 | manner which does not create potential liability for other Contributors. 118 | Therefore, if a Contributor includes the Program in a commercial product 119 | offering, such Contributor ("Commercial Contributor") hereby agrees to defend 120 | and indemnify every other Contributor ("Indemnified Contributor") against any 121 | losses, damages and costs (collectively "Losses") arising from claims, 122 | lawsuits and other legal actions brought by a third party against the 123 | Indemnified Contributor to the extent caused by the acts or omissions of such 124 | Commercial Contributor in connection with its distribution of the Program in 125 | a commercial product offering. The obligations in this section do not apply 126 | to any claims or Losses relating to any actual or alleged intellectual 127 | property infringement. In order to qualify, an Indemnified Contributor must: 128 | a) promptly notify the Commercial Contributor in writing of such claim, and 129 | b) allow the Commercial Contributor tocontrol, and cooperate with the 130 | Commercial Contributor in, the defense and any related settlement 131 | negotiations. The Indemnified Contributor may participate in any such claim 132 | at its own expense. 133 | 134 | For example, a Contributor might include the Program in a commercial product 135 | offering, Product X. That Contributor is then a Commercial Contributor. If 136 | that Commercial Contributor then makes performance claims, or offers 137 | warranties related to Product X, those performance claims and warranties are 138 | such Commercial Contributor's responsibility alone. Under this section, the 139 | Commercial Contributor would have to defend claims against the other 140 | Contributors related to those performance claims and warranties, and if a 141 | court requires any other Contributor to pay any damages as a result, the 142 | Commercial Contributor must pay those damages. 143 | 144 | 5. NO WARRANTY 145 | 146 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON 147 | AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER 148 | EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR 149 | CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A 150 | PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the 151 | appropriateness of using and distributing the Program and assumes all risks 152 | associated with its exercise of rights under this Agreement , including but 153 | not limited to the risks and costs of program errors, compliance with 154 | applicable laws, damage to or loss of data, programs or equipment, and 155 | unavailability or interruption of operations. 156 | 157 | 6. DISCLAIMER OF LIABILITY 158 | 159 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 160 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 161 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION 162 | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 163 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 164 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 165 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY 166 | OF SUCH DAMAGES. 167 | 168 | 7. GENERAL 169 | 170 | If any provision of this Agreement is invalid or unenforceable under 171 | applicable law, it shall not affect the validity or enforceability of the 172 | remainder of the terms of this Agreement, and without further action by the 173 | parties hereto, such provision shall be reformed to the minimum extent 174 | necessary to make such provision valid and enforceable. 175 | 176 | If Recipient institutes patent litigation against any entity (including a 177 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 178 | (excluding combinations of the Program with other software or hardware) 179 | infringes such Recipient's patent(s), then such Recipient's rights granted 180 | under Section 2(b) shall terminate as of the date such litigation is filed. 181 | 182 | All Recipient's rights under this Agreement shall terminate if it fails to 183 | comply with any of the material terms or conditions of this Agreement and 184 | does not cure such failure in a reasonable period of time after becoming 185 | aware of such noncompliance. If all Recipient's rights under this Agreement 186 | terminate, Recipient agrees to cease use and distribution of the Program as 187 | soon as reasonably practicable. However, Recipient's obligations under this 188 | Agreement and any licenses granted by Recipient relating to the Program shall 189 | continue and survive. 190 | 191 | Everyone is permitted to copy and distribute copies of this Agreement, but in 192 | order to avoid inconsistency the Agreement is copyrighted and may only be 193 | modified in the following manner. The Agreement Steward reserves the right to 194 | publish new versions (including revisions) of this Agreement from time to 195 | time. No one other than the Agreement Steward has the right to modify this 196 | Agreement. The Eclipse Foundation is the initial Agreement Steward. The 197 | Eclipse Foundation may assign the responsibility to serve as the Agreement 198 | Steward to a suitable separate entity. Each new version of the Agreement will 199 | be given a distinguishing version number. The Program (including 200 | Contributions) may always be distributed subject to the version of the 201 | Agreement under which it was received. In addition, after a new version of 202 | the Agreement is published, Contributor may elect to distribute the Program 203 | (including its Contributions) under the new version. Except as expressly 204 | stated in Sections 2(a) and 2(b) above, Recipient receives no rights or 205 | licenses to the intellectual property of any Contributor under this 206 | Agreement, whether expressly, by implication, estoppel or otherwise. All 207 | rights in the Program not expressly granted under this Agreement are 208 | reserved. 209 | 210 | This Agreement is governed by the laws of the State of New York and the 211 | intellectual property laws of the United States of America. No party to this 212 | Agreement will bring a legal action under this Agreement more than one year 213 | after the cause of action arose. Each party waives its rights to a jury trial 214 | in any resulting litigation. 215 | -------------------------------------------------------------------------------- /06/the-divine-cheese-code/README.md: -------------------------------------------------------------------------------- 1 | # the-divine-cheese-code 2 | 3 | FIXME: description 4 | 5 | ## Installation 6 | 7 | Download from http://example.com/FIXME. 8 | 9 | ## Usage 10 | 11 | FIXME: explanation 12 | 13 | $ java -jar the-divine-cheese-code-0.1.0-standalone.jar [args] 14 | 15 | ## Options 16 | 17 | FIXME: listing of options this app accepts. 18 | 19 | ## Examples 20 | 21 | ... 22 | 23 | ### Bugs 24 | 25 | ... 26 | 27 | ### Any Other Sections 28 | ### That You Think 29 | ### Might be Useful 30 | 31 | ## License 32 | 33 | Copyright © 2015 FIXME 34 | 35 | Distributed under the Eclipse Public License either version 1.0 or (at 36 | your option) any later version. 37 | -------------------------------------------------------------------------------- /06/the-divine-cheese-code/doc/intro.md: -------------------------------------------------------------------------------- 1 | # Introduction to the-divine-cheese-code 2 | 3 | TODO: write [great documentation](http://jacobian.org/writing/what-to-write/) 4 | -------------------------------------------------------------------------------- /06/the-divine-cheese-code/project.clj: -------------------------------------------------------------------------------- 1 | (defproject the-divine-cheese-code "0.1.0-SNAPSHOT" 2 | :description "FIXME: write description" 3 | :url "http://example.com/FIXME" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :dependencies [[org.clojure/clojure "1.6.0"]] 7 | :main ^:skip-aot the-divine-cheese-code.core 8 | :target-path "target/%s" 9 | :profiles {:uberjar {:aot :all}}) 10 | -------------------------------------------------------------------------------- /06/the-divine-cheese-code/src/the_divine_cheese_code/core.clj: -------------------------------------------------------------------------------- 1 | (ns the-divine-cheese-code.core 2 | (:require [clojure.java.browse :as browse] 3 | [the-divine-cheese-code.visualization.svg :refer [xml]]) 4 | (:gen-class)) 5 | 6 | (def heists [{:location "Cologne, Germany" 7 | :cheese-name "Archbishop Hildebold's Cheese Pretzel" 8 | :lat 50.95 9 | :lng 6.97} 10 | {:location "Zurich, Switzerland" 11 | :cheese-name "The Standard Emmental" 12 | :lat 47.37 13 | :lng 8.55} 14 | {:location "Marseille, France" 15 | :cheese-name "Le Fromage de Cosquer" 16 | :lat 43.30 17 | :lng 5.37} 18 | {:location "Zurich, Switzerland" 19 | :cheese-name "The Lesser Emmental" 20 | :lat 47.37 21 | :lng 8.55} 22 | {:location "Vatican City" 23 | :cheese-name "The Cheese of Turin" 24 | :lat 41.90 25 | :lng 12.45}]) 26 | 27 | (defn url 28 | [filename] 29 | (str "file:///" 30 | (System/getProperty "user.dir") 31 | "/" 32 | filename)) 33 | 34 | (defn template 35 | [contents] 36 | (str "" 37 | contents)) 38 | 39 | (defn -main 40 | [& args] 41 | (let [filename "map.html"] 42 | (->> heists 43 | (xml 50 100) 44 | template 45 | (spit filename)) 46 | (browse/browse-url (url filename)))) 47 | -------------------------------------------------------------------------------- /06/the-divine-cheese-code/src/the_divine_cheese_code/visualization/svg.clj: -------------------------------------------------------------------------------- 1 | (ns the-divine-cheese-code.visualization.svg 2 | (:require [clojure.string :as s]) 3 | (:refer-clojure :exclude [min max])) 4 | 5 | (defn comparator-over-maps 6 | [comparison-fn ks] 7 | (fn [maps] 8 | (zipmap ks 9 | (map (fn [k] (apply comparison-fn (map k maps))) 10 | ks)))) 11 | 12 | (def min (comparator-over-maps clojure.core/min [:lat :lng])) 13 | (def max (comparator-over-maps clojure.core/max [:lat :lng])) 14 | 15 | 16 | (defn translate-to-00 17 | [locations] 18 | (let [mincoords (min locations)] 19 | (map #(merge-with - % mincoords) locations))) 20 | 21 | (defn scale 22 | [width height locations] 23 | (let [maxcoords (max locations) 24 | ratio {:lat (/ height (:lat maxcoords)) 25 | :lng (/ width (:lng maxcoords))}] 26 | (map #(merge-with * % ratio) locations))) 27 | 28 | (defn latlng->point 29 | "Convert lat/lng map to comma-separated string" 30 | [latlng] 31 | (str (:lat latlng) "," (:lng latlng))) 32 | 33 | (defn points 34 | [locations] 35 | (clojure.string/join " " (map latlng->point locations))) 36 | 37 | (defn line 38 | [points] 39 | (str "")) 40 | 41 | (defn transform 42 | "Just chains other functions" 43 | [width height locations] 44 | (->> locations 45 | translate-to-00 46 | (scale width height))) 47 | 48 | (defn xml 49 | "svg 'template', which also flips the coordinate system" 50 | [width height locations] 51 | (str "" 52 | ;; These two tags change the coordinate system so that 53 | ;; 0,0 is in the lower-left corner, instead of SVG's default 54 | ;; upper-left corner 55 | "" 56 | "" 57 | (-> (transform width height locations) 58 | points 59 | line) 60 | "" 61 | "")) 62 | -------------------------------------------------------------------------------- /06/the-divine-cheese-code/test/the_divine_cheese_code/core_test.clj: -------------------------------------------------------------------------------- 1 | (ns the-divine-cheese-code.core-test 2 | (:require [clojure.test :refer :all] 3 | [the-divine-cheese-code.core :refer :all])) 4 | 5 | (deftest a-test 6 | (testing "FIXME, I fail." 7 | (is (= 0 1)))) 8 | -------------------------------------------------------------------------------- /07/README.md: -------------------------------------------------------------------------------- 1 | # Chapter 7: Clojure Alchemy: Reading, Evaluation, and Macros 2 | 3 | [`code.clj`](code.clj) has all the code from the chapter. 4 | -------------------------------------------------------------------------------- /07/code.clj: -------------------------------------------------------------------------------- 1 | (defmacro backwards 2 | [form] 3 | (reverse form)) 4 | 5 | (backwards (" backwards" " am" "I" str)) 6 | ; => "I am backwards" 7 | 8 | 9 | (def addition-list (list + 1 2)) 10 | (eval addition-list) 11 | ; => 3 12 | 13 | (eval (concat addition-list [10])) 14 | ; => 13 15 | 16 | (eval (list 'def 'lucky-number (concat addition-list [10]))) 17 | ; => #'user/lucky-number 18 | 19 | lucky-number 20 | ; => 13 21 | 22 | 23 | (str "To understand what recursion is," " you must first understand recursion.") 24 | "To understand what recursion is, you must first understand recursion." 25 | 26 | (read-string "(+ 1 2)") 27 | ; => (+ 1 2) 28 | 29 | (list? (read-string "(+ 1 2)")) 30 | ; => true 31 | 32 | (conj (read-string "(+ 1 2)") :zagglewag) 33 | ; => (:zagglewag + 1 2) 34 | 35 | 36 | (eval (read-string "(+ 1 2)")) 37 | ; => 3 38 | 39 | (#(+ 1 %) 3) 40 | ; => 4 41 | 42 | (read-string "#(+ 1 %)") 43 | ; => (fn* [p1__423#] (+ 1 p1__423#)) 44 | 45 | (read-string "'(a b c)") 46 | ; => (quote (a b c)) 47 | 48 | (read-string "@var") 49 | ; => (clojure.core/deref var) 50 | 51 | (read-string "; ignore!\n(+ 1 2)") 52 | ; => (+ 1 2) 53 | 54 | true 55 | ; => true 56 | 57 | false 58 | ; => false 59 | 60 | {} 61 | ; => {} 62 | 63 | :huzzah 64 | ; => :huzzah 65 | 66 | () 67 | ; => () 68 | 69 | (if true :a :b) 70 | ; => :a 71 | 72 | if 73 | ; => CompilerException java.lang.RuntimeException: Unable to resolve symbol: if in this context, compiling:(NO_SOURCE_PATH:0:0) 74 | 75 | (let [x 5] 76 | (+ x 3)) 77 | ; => 8 78 | 79 | (def x 15) 80 | (+ x 3) 81 | ; => 18 82 | 83 | (def x 15) 84 | (let [x 5] 85 | (+ x 3)) 86 | ; => 8 87 | 88 | (let [x 5] 89 | (let [x 6] 90 | (+ x 3))) 91 | ; => 9 92 | 93 | (defn exclaim 94 | [exclamation] 95 | (str exclamation "!")) 96 | 97 | (exclaim "Hadoken") 98 | ; => "Hadoken!" 99 | 100 | (map inc [1 2 3]) 101 | ; => (2 3 4) 102 | 103 | (read-string ("+")) 104 | ; => + 105 | 106 | (type (read-string "+")) 107 | ; => clojure.lang.Symbol 108 | 109 | (list (read-string "+") 1 2) 110 | ; => (+ 1 2) 111 | 112 | (eval (list (read-string "+") 1 2)) 113 | ; => 3 114 | 115 | (eval (read-string "()")) 116 | ; => () 117 | 118 | (+ 1 2) 119 | ; => 3 120 | 121 | (+ 1 (+ 2 3)) 122 | ; => 6 123 | 124 | (if true 1 2) 125 | ; => 1 126 | 127 | '(a b c) 128 | 129 | (quote (a b c)) 130 | 131 | (read-string "(1 + 1)") 132 | ; => (1 + 1) 133 | 134 | (eval (read-string "(1 + 1)")) 135 | ; => ClassCastException java.lang.Long cannot be cast to clojure.lang.IFn 136 | 137 | (let [infix (read-string "(1 + 1)")] 138 | (list (second infix) (first infix) (last infix))) 139 | ; => (+ 1 1) 140 | 141 | (eval 142 | (let [infix (read-string "(1 + 1)")] 143 | (list (second infix) (first infix) (last infix)))) 144 | ; => 2 145 | 146 | (defmacro ignore-last-operand 147 | [function-call] 148 | (butlast function-call)) 149 | 150 | (ignore-last-operand (+ 1 2 10)) 151 | ; => 3 152 | 153 | ;; This will not print anything 154 | (ignore-last-operand (+ 1 2 (println "look at me!!!"))) 155 | ; => 3 156 | 157 | (macroexpand '(ignore-last-operand (+ 1 2 10))) 158 | ; => (+ 1 2) 159 | 160 | (macroexpand '(ignore-last-operand (+ 1 2 (println "look at me!!!")))) 161 | ; => (+ 1 2) 162 | 163 | (defmacro infix 164 | [infixed] 165 | (list (second infixed) 166 | (first infixed) 167 | (last infixed))) 168 | 169 | (infix (1 + 2)) 170 | ; => 3 171 | 172 | (defn read-resource 173 | "Read a resource into a string" 174 | [path] 175 | (read-string (slurp (clojure.java.io/resource path)))) 176 | 177 | (defn read-resource 178 | [path] 179 | (-> path 180 | clojure.java.io/resource 181 | slurp 182 | read-string)) 183 | -------------------------------------------------------------------------------- /08/README.md: -------------------------------------------------------------------------------- 1 | # Chapter 8: Writing Macros 2 | 3 | [`code.clj`](code.clj) has all the code from the chapter. 4 | -------------------------------------------------------------------------------- /08/code.clj: -------------------------------------------------------------------------------- 1 | (macroexpand '(when boolean-expression 2 | expression-1 3 | expression-2 4 | expression-3)) 5 | ; => (if boolean-expression 6 | ; => (do expression-1 7 | ; => expression-2 8 | ; => expression-3)) 9 | 10 | 11 | (defmacro infix 12 | "Use this macro when you pine for the notation of your childhood" 13 | [infixed] 14 | (list (second infixed) (first infixed) (last infixed))) 15 | 16 | 17 | (infix (1 + 1)) 18 | ; => 2 19 | 20 | 21 | (macroexpand '(infix (1 + 1))) 22 | ; => (+ 1 1) 23 | 24 | 25 | (defmacro infix-2 26 | [[operand1 op operand2]] 27 | (list op operand1 operand2)) 28 | 29 | 30 | (defmacro and 31 | "Evaluates exprs one at a time, from left to right. If a form 32 | returns logical false (nil or false), and returns that value and 33 | doesn't evaluate any of the other expressions, otherwise it returns 34 | the value of the last expr. (and) returns true." 35 | {:added "1.0"} 36 | ([] true) 37 | ([x] x) 38 | ([x & next] 39 | `(let [and# ~x] 40 | (if and# (and ~@next) and#)))) 41 | 42 | 43 | (let [result expression] 44 | (println result) 45 | result) 46 | 47 | 48 | (defmacro my-print-whoopsie 49 | [expression] 50 | (list let [result expression] 51 | (list println result) 52 | result)) 53 | 54 | 55 | (defmacro my-print 56 | [expression] 57 | (list 'let ['result expression] 58 | (list 'println 'result) 59 | 'result)) 60 | 61 | 62 | (+ 1 2) 63 | ; => 3 64 | 65 | 66 | (quote (+ 1 2)) 67 | ; => (+ 1 2) 68 | 69 | 70 | + 71 | ; => # 72 | 73 | 74 | (quote +) 75 | ; => + 76 | 77 | 78 | sweating-to-the-oldies 79 | ; => Unable to resolve symbol: sweating-to-the-oldies in this context 80 | 81 | 82 | (quote sweating-to-the-oldies) 83 | ; => sweating-to-the-oldies 84 | 85 | 86 | '(+ 1 2) 87 | ; => (+ 1 2) 88 | 89 | 'dr-jekyll-and-richard-simmons 90 | ; => dr-jekyll-and-richard-simmons 91 | 92 | 93 | (defmacro when 94 | "Evaluates test. If logical true, evaluates body in an implicit do." 95 | {:added "1.0"} 96 | [test & body] 97 | (list 'if test (cons 'do body))) 98 | 99 | 100 | (macroexpand '(when (the-cows-come :home) 101 | (call me :pappy) 102 | (slap me :silly))) 103 | ; => (if (the-cows-come :home) 104 | (do (call me :pappy) 105 | (slap me :silly))) 106 | 107 | 108 | (defmacro unless 109 | "Inverted 'if'" 110 | [test & branches] 111 | (conj (reverse branches) test 'if)) 112 | 113 | 114 | (macroexpand '(unless (done-been slapped? me) 115 | (slap me :silly) 116 | (say "I reckon that'll learn me"))) 117 | ; => (if (done-been slapped? me) 118 | (say "I reckon that'll learn me") 119 | (slap me :silly)) 120 | 121 | 122 | '+ 123 | ; => + 124 | 125 | 126 | 'clojure.core/+ 127 | ; => clojure.core/+ 128 | 129 | 130 | `+ 131 | ; => clojure.core/+ 132 | 133 | 134 | '(+ 1 2) 135 | ; => (+ 1 2) 136 | 137 | 138 | `(+ 1 2) 139 | ; => (clojure.core/+ 1 2) 140 | 141 | 142 | `(+ 1 ~(inc 1)) 143 | ; => (clojure.core/+ 1 2) 144 | 145 | 146 | `(+ 1 (inc 1)) 147 | ; => (clojure.core/+ 1 (clojure.core/inc 1)) 148 | 149 | 150 | (list '+ 1 (inc 1)) 151 | ; => (+ 1 2) 152 | 153 | `(+ 1 ~(inc 1)) 154 | ; => (clojure.core/+ 1 2) 155 | 156 | 157 | (defmacro code-critic 158 | "Phrases are courtesy Hermes Conrad from Futurama" 159 | [bad good] 160 | (list 'do 161 | (list 'println 162 | "Great squid of Madrid, this is bad code:" 163 | (list 'quote bad)) 164 | (list 'println 165 | "Sweet gorilla of Manila, this is good code:" 166 | (list 'quote good)))) 167 | 168 | (code-critic (1 + 1) (+ 1 1)) 169 | ; => Great squid of Madrid, this is bad code: (1 + 1) 170 | ; => Sweet gorilla of Manila, this is good code: (+ 1 1) 171 | 172 | 173 | (defmacro code-critic 174 | "Phrases are courtesy Hermes Conrad from Futurama" 175 | [bad good] 176 | `(do (println "Great squid of Madrid, this is bad code:" 177 | (quote ~bad)) 178 | (println "Sweet gorilla of Manila, this is good code:" 179 | (quote ~good)))) 180 | 181 | 182 | (defn criticize-code 183 | [criticism code] 184 | `(println ~criticism (quote ~code))) 185 | 186 | (defmacro code-critic 187 | [bad good] 188 | `(do ~(criticize-code "Cursed bacteria of Liberia, this is bad code:" bad) 189 | ~(criticize-code "Sweet sacred boa of Western and Eastern Samoa, this is good code:" good))) 190 | 191 | 192 | (defmacro code-critic 193 | [bad good] 194 | `(do ~(map #(apply criticize-code %) 195 | [["Great squid of Madrid, this is bad code:" bad] 196 | ["Sweet gorilla of Manila, this is good code:" good]]))) 197 | 198 | 199 | (code-critic (1 + 1) (+ 1 1)) 200 | ; => NullPointerException 201 | 202 | 203 | (do 204 | ((clojure.core/println "criticism" '(1 + 1)) 205 | (clojure.core/println "criticism" '(+ 1 1)))) 206 | 207 | 208 | (do 209 | (nil 210 | (clojure.core/println "criticism" '(+ 1 1)))) 211 | 212 | 213 | (do 214 | (nil nil)) 215 | 216 | 217 | `(+ ~(list 1 2 3)) 218 | ; => (clojure.core/+ (1 2 3)) 219 | 220 | 221 | `(+ ~@(list 1 2 3)) 222 | ; => (clojure.core/+ 1 2 3) 223 | 224 | 225 | (defmacro code-critic 226 | [{:keys [good bad]}] 227 | `(do ~@(map #(apply criticize-code %) 228 | [["Sweet lion of Zion, this is bad code:" bad] 229 | ["Great cow of Moscow, this is good code:" good]]))) 230 | 231 | (code-critic {:good (+ 1 1) :bad (1 + 1)}) 232 | ; => Sweet lion of Zion, this is bad code: (1 + 1) 233 | ; => Great cow of Moscow, this is good code: (+ 1 1) 234 | 235 | 236 | (def message "Good job!") 237 | (defmacro with-mischief 238 | [& stuff-to-do] 239 | (concat (list 'let ['message "Oh, big deal!"]) 240 | stuff-to-do)) 241 | 242 | (with-mischief 243 | (println "Here's how I feel about that thing you did: " message)) 244 | ; => Here's how I feel about that thing you did: Oh, big deal! 245 | 246 | 247 | (def message "Good job!") 248 | (defmacro with-mischief 249 | [& stuff-to-do] 250 | `(let [message "Oh, big deal!"] 251 | ~@stuff-to-do)) 252 | 253 | (with-mischief 254 | (println "Here's how I feel about that thing you did: " message)) 255 | ; Exception: Can't let qualified name: user/message 256 | 257 | 258 | (gensym) 259 | ; => G__655 260 | 261 | (gensym 'message) 262 | ; => message4760 263 | 264 | (defmacro without-mischief 265 | [& stuff-to-do] 266 | (let [macro-message (gensym 'message)] 267 | `(let [~macro-message "Oh, big deal!"] 268 | ~@stuff-to-do 269 | (println "I still need to say: " ~macro-message)))) 270 | 271 | (without-mischief 272 | (println "Here's how I feel about that thing you did: " message)) 273 | ; => Here's how I feel about that thing you did: Good job! 274 | ; => I still need to say: Oh, big deal! 275 | 276 | 277 | `(blarg# blarg#) 278 | (blarg__2869__auto__ blarg__2869__auto__) 279 | 280 | `(let [name# "Larry Potter"] name#) 281 | ; => (clojure.core/let [name__2872__auto__ "Larry Potter"] name__2872__auto__) 282 | 283 | 284 | (defmacro report 285 | [to-try] 286 | `(if ~to-try 287 | (println (quote ~to-try) "was successful:" ~to-try) 288 | (println (quote ~to-try) "was not successful:" ~to-try))) 289 | 290 | ;; Thread/sleep takes a number of milliseconds to sleep for 291 | (report (do (Thread/sleep 1000) (+ 1 1))) 292 | 293 | 294 | (if (do (Thread/sleep 1000) (+ 1 1)) 295 | (println '(do (Thread/sleep 1000) (+ 1 1)) 296 | "was successful:" 297 | (do (Thread/sleep 1000) (+ 1 1))) 298 | 299 | (println '(do (Thread/sleep 1000) (+ 1 1)) 300 | "was not successful:" 301 | (do (Thread/sleep 1000) (+ 1 1)))) 302 | 303 | 304 | (defmacro report 305 | [to-try] 306 | `(let [result# ~to-try] 307 | (if result# 308 | (println (quote ~to-try) "was successful:" result#) 309 | (println (quote ~to-try) "was not successful:" result#)))) 310 | 311 | 312 | (report (= 1 1)) 313 | ; => (= 1 1) was successful: true 314 | 315 | (report (= 1 2)) 316 | ; => (= 1 2) was not successful: false 317 | 318 | 319 | (doseq [code ['(= 1 1) '(= 1 2)]] 320 | (report code)) 321 | ; => code was successful: (= 1 1) 322 | ; => code was successful: (= 1 2) 323 | 324 | 325 | (if 326 | code 327 | (clojure.core/println 'code "was successful:" code) 328 | (clojure.core/println 'code "was not successful:" code)) 329 | 330 | 331 | (defmacro doseq-macro 332 | [macroname & args] 333 | `(do 334 | ~@(map (fn [arg] (list macroname arg)) args))) 335 | 336 | (doseq-macro report (= 1 1) (= 1 2)) 337 | ; => (= 1 1) was successful: true 338 | ; => (= 1 2) was not successful: false 339 | 340 | 341 | (def order-details 342 | {:name "Mitchard Blimmons" 343 | :email "mitchard.blimmonsgmail.com"}) 344 | 345 | 346 | (validate order-details order-details-validations) 347 | ; => {:email ["Your email address doesn't look like an email address."]} 348 | 349 | 350 | (def order-details-validations 351 | {:name 352 | ["Please enter a name" not-empty] 353 | 354 | :email 355 | ["Please enter an email address" not-empty 356 | 357 | "Your email address doesn't look like an email address" 358 | #(or (empty? %) (re-seq #"@" %))]}) 359 | 360 | 361 | (defn error-messages-for 362 | "Return a seq of error messages" 363 | [to-validate message-validator-pairs] 364 | (map first (filter #(not ((second %) to-validate)) 365 | (partition 2 message-validator-pairs)))) 366 | 367 | 368 | (error-messages-for "" ["Please enter a name" not-empty]) 369 | ; => ("Please enter a name") 370 | 371 | 372 | (defn validate 373 | "Returns a map with a vector of errors for each key" 374 | [to-validate validations] 375 | (reduce (fn [errors validation] 376 | (let [[fieldname validation-check-groups] validation 377 | value (get to-validate fieldname) 378 | error-messages (error-messages-for value validation-check-groups)] 379 | (if (empty? error-messages) 380 | errors 381 | (assoc errors fieldname error-messages)))) 382 | {} 383 | validations)) 384 | 385 | (validate order-details order-details-validations) 386 | ; => {:email ["Your email address doesn't look like an email address"]} 387 | 388 | 389 | (let [errors (validate order-details order-details-validations)] 390 | (if (empty? errors) 391 | (println :success) 392 | (println :failure errors))) 393 | 394 | 395 | (defn if-valid 396 | [record validations success-code failure-code] 397 | (let [errors (validate record validations)] 398 | (if (empty? errors) 399 | success-code 400 | failure-code))) 401 | 402 | 403 | (if-valid order-details order-details-validations errors 404 | (render :success) 405 | (render :failure errors)) 406 | 407 | 408 | (defmacro if-valid 409 | "Handle validation more concisely" 410 | [to-validate validations errors-name & then-else] 411 | `(let [~errors-name (validate ~to-validate ~validations)] 412 | (if (empty? ~errors-name) 413 | ~@then-else))) 414 | 415 | 416 | (macroexpand 417 | '(if-valid order-details order-details-validations my-error-name 418 | (println :success) 419 | (println :failure my-error-name))) 420 | (let* 421 | [my-error-name (user/validate order-details order-details-validations)] 422 | (if (clojure.core/empty? my-error-name) 423 | (println :success) 424 | (println :failure my-error-name))) 425 | -------------------------------------------------------------------------------- /09/README.md: -------------------------------------------------------------------------------- 1 | # Chapter 9: The Sacred Art of Concurrent and Parallel Programming 2 | 3 | [`code.clj`](code.clj) has all the code from the chapter. 4 | -------------------------------------------------------------------------------- /09/code.clj: -------------------------------------------------------------------------------- 1 | (future (Thread/sleep 4000) 2 | (println "I'll print after 4 seconds")) 3 | (println "I'll print immediately") 4 | 5 | 6 | (let [result (future (println "this prints once") 7 | (+ 1 1))] 8 | (println "deref: " (deref result)) 9 | (println "@: " @result)) 10 | ; => "this prints once" 11 | ; => deref: 2 12 | ; => @: 2 13 | 14 | 15 | (let [result (future (Thread/sleep 3000) 16 | (+ 1 1))] 17 | (println "The result is: " @result) 18 | (println "It will be at least 3 seconds before I print")) 19 | ; => The result is: 2 20 | ; => It will be at least 3 seconds before I print 21 | 22 | 23 | (deref (future (Thread/sleep 1000) 0) 10 5) 24 | ; => 5 25 | 26 | 27 | (realized? (future (Thread/sleep 1000))) 28 | ; => false 29 | 30 | (let [f (future)] 31 | @f 32 | (realized? f)) 33 | ; => true 34 | 35 | 36 | (def jackson-5-delay 37 | (delay (let [message "Just call my name and I'll be there"] 38 | (println "First deref:" message) 39 | message))) 40 | 41 | (force jackson-5-delay) 42 | ; => First deref: Just call my name and I'll be there 43 | ; => "Just call my name and I'll be there" 44 | 45 | 46 | @jackson-5-delay 47 | ; => "Just call my name and I'll be there" 48 | 49 | 50 | (def gimli-headshots ["serious.jpg" "fun.jpg" "playful.jpg"]) 51 | (defn email-user 52 | [email-address] 53 | (println "Sending headshot notification to" email-address)) 54 | (defn upload-document 55 | "Needs to be implemented" 56 | [headshot] 57 | true) 58 | (let [notify (delay (email-user "and-my-axe@gmail.com"))] 59 | (doseq [headshot gimli-headshots] 60 | (future (upload-document headshot) 61 | (force notify)))) 62 | 63 | 64 | (def my-promise (promise)) 65 | (deliver my-promise (+ 1 2)) 66 | @my-promise 67 | ; => 3 68 | 69 | 70 | (def yak-butter-international 71 | {:store "Yak Butter International" 72 | :price 90 73 | :smoothness 90}) 74 | (def butter-than-nothing 75 | {:store "Butter Than Nothing" 76 | :price 150 77 | :smoothness 83}) 78 | ;; This is the butter that meets our requirements 79 | (def baby-got-yak 80 | {:store "Baby Got Yak" 81 | :price 94 82 | :smoothness 99}) 83 | 84 | (defn mock-api-call 85 | [result] 86 | (Thread/sleep 1000) 87 | result) 88 | 89 | (defn satisfactory? 90 | "If the butter meets our criteria, return the butter, else return false" 91 | [butter] 92 | (and (<= (:price butter) 100) 93 | (>= (:smoothness butter) 97) 94 | butter)) 95 | 96 | 97 | (time (some (comp satisfactory? mock-api-call) 98 | [yak-butter-international butter-than-nothing baby-got-yak])) 99 | ; => "Elapsed time: 3002.132 msecs" 100 | ; => {:store "Baby Got Yak", :smoothness 99, :price 94} 101 | 102 | 103 | (time 104 | (let [butter-promise (promise)] 105 | (doseq [butter [yak-butter-international butter-than-nothing baby-got-yak]] 106 | (future (if-let [satisfactory-butter (satisfactory? (mock-api-call butter))] 107 | (deliver butter-promise satisfactory-butter)))) 108 | (println "And the winner is:" @butter-promise))) 109 | ; => "Elapsed time: 1002.652 msecs" 110 | ; => And the winner is: {:store Baby Got Yak, :smoothness 99, :price 94} 111 | 112 | 113 | (let [p (promise)] 114 | (deref p 100 "timed out")) 115 | 116 | 117 | (let [ferengi-wisdom-promise (promise)] 118 | (future (println "Here's some Ferengi wisdom:" @ferengi-wisdom-promise)) 119 | (Thread/sleep 100) 120 | (deliver ferengi-wisdom-promise "Whisper your way to success.")) 121 | ; => Here's some Ferengi wisdom: Whisper your way to success. 122 | 123 | 124 | (defmacro wait 125 | "Sleep `timeout` seconds before evaluating body" 126 | [timeout & body] 127 | `(do (Thread/sleep ~timeout) ~@body)) 128 | 129 | 130 | (let [saying3 (promise)] 131 | (future (deliver saying3 (wait 100 "Cheerio!"))) 132 | @(let [saying2 (promise)] 133 | (future (deliver saying2 (wait 400 "Pip pip!"))) 134 | @(let [saying1 (promise)] 135 | (future (deliver saying1 (wait 200 "'Ello, gov'na!"))) 136 | (println @saying1) 137 | saying1) 138 | (println @saying2) 139 | saying2) 140 | (println @saying3) 141 | saying3) 142 | 143 | 144 | (-> (enqueue saying (wait 200 "'Ello, gov'na!") (println @saying)) 145 | (enqueue saying (wait 400 "Pip pip!") (println @saying)) 146 | (enqueue saying (wait 100 "Cheerio!") (println @saying))) 147 | 148 | 149 | (defmacro enqueue 150 | ([q concurrent-promise-name concurrent serialized] 151 | `(let [~concurrent-promise-name (promise)] 152 | (future (deliver ~concurrent-promise-name ~concurrent)) 153 | (deref ~q) 154 | ~serialized 155 | ~concurrent-promise-name)) 156 | ([concurrent-promise-name concurrent serialized] 157 | `(enqueue (future) ~concurrent-promise-name ~concurrent ~serialized))) 158 | 159 | 160 | (time @(-> (enqueue saying (wait 200 "'Ello, gov'na!") (println @saying)) 161 | (enqueue saying (wait 400 "Pip pip!") (println @saying)) 162 | (enqueue saying (wait 100 "Cheerio!") (println @saying)))) 163 | ; => 'Ello, gov'na! 164 | ; => Pip pip! 165 | ; => Cheerio! 166 | ; => "Elapsed time: 401.635 msecs" 167 | -------------------------------------------------------------------------------- /10/README.md: -------------------------------------------------------------------------------- 1 | # Chapter 10: Clojure Metaphysics: Atoms, Refs, Vars, and Cuddle Zombies 2 | 3 | [`code.clj`](code.clj) has all the Clojure code from the chapter. 4 | 5 | [`code.rb`](code.rb) has some Ruby stuff 6 | -------------------------------------------------------------------------------- /10/code.clj: -------------------------------------------------------------------------------- 1 | (def fred (atom {:cuddle-hunger-level 0 2 | :percent-deteriorated 0})) 3 | @fred 4 | ; => {:cuddle-hunger-level 0, :percent-deteriorated 0} 5 | 6 | (let [zombie-state @fred] 7 | (if (>= (:percent-deteriorated zombie-state) 50) 8 | (future (println (:percent-deteriorated zombie-state))))) 9 | 10 | (swap! fred 11 | (fn [current-state] 12 | (merge-with + current-state {:cuddle-hunger-level 1}))) 13 | ; => {:cuddle-hunger-level 1, :percent-deteriorated 0} 14 | 15 | @fred 16 | ; => {:cuddle-hunger-level 1, :percent-deteriorated 0} 17 | 18 | (swap! fred 19 | (fn [current-state] 20 | (merge-with + current-state {:cuddle-hunger-level 1 21 | :percent-deteriorated 1}))) 22 | ; => {:cuddle-hunger-level 2, :percent-deteriorated 1} 23 | 24 | (defn increase-cuddle-hunger-level 25 | [zombie-state increase-by] 26 | (merge-with + zombie-state {:cuddle-hunger-level increase-by})) 27 | 28 | (increase-cuddle-hunger-level @fred 10) 29 | ; => {:cuddle-hunger-level 12, :percent-deteriorated 1} 30 | 31 | (swap! fred increase-cuddle-hunger-level 10) 32 | ; => {:cuddle-hunger-level 12, :percent-deteriorated 1} 33 | 34 | @fred 35 | ; => {:cuddle-hunger-level 12, :percent-deteriorated 1} 36 | 37 | (update-in {:a {:b 3}} [:a :b] inc) 38 | ; => {:a {:b 4}} 39 | 40 | (update-in {:a {:b 3}} [:a :b] + 10) 41 | ; => {:a {:b 13}} 42 | 43 | (swap! fred update-in [:cuddle-hunger-level] + 10) 44 | ; => {:cuddle-hunger-level 22, :percent-deteriorated 1} 45 | 46 | (let [num (atom 1) 47 | s1 @num] 48 | (swap! num inc) 49 | (println "State 1:" s1) 50 | (println "Current state:" @num)) 51 | ; => State 1: 1 52 | ; => Current state: 2 53 | 54 | (reset! fred {:cuddle-hunger-level 0 55 | :percent-deteriorated 0}) 56 | 57 | (defn shuffle-speed 58 | [zombie] 59 | (* (:cuddle-hunger-level zombie) 60 | (- 100 (:percent-deteriorated zombie)))) 61 | 62 | (defn shuffle-alert 63 | [key watched old-state new-state] 64 | (let [sph (shuffle-speed new-state)] 65 | (if (> sph 5000) 66 | (do 67 | (println "Run, you fool!") 68 | (println "The zombie's SPH is now " sph) 69 | (println "This message brought to your courtesy of " key)) 70 | (do 71 | (println "All's well with " key) 72 | (println "Cuddle hunger: " (:cuddle-hunger-level new-state)) 73 | (println "Percent deteriorated: " (:percent-deteriorated new-state)) 74 | (println "SPH: " sph))))) 75 | 76 | (reset! fred {:cuddle-hunger-level 22 77 | :percent-deteriorated 2}) 78 | (add-watch fred :fred-shuffle-alert shuffle-alert) 79 | (swap! fred update-in [:percent-deteriorated] + 1) 80 | ; => All's well with :fred-shuffle-alert 81 | ; => Cuddle hunger: 22 82 | ; => Percent deteriorated: 3 83 | ; => SPH: 2134 84 | 85 | (swap! fred update-in [:cuddle-hunger-level] + 30) 86 | ; => Run, you fool! 87 | ; => The zombie's SPH is now 5044 88 | ; => This message brought to your courtesy of :fred-shuffle-alert 89 | 90 | (defn percent-deteriorated-validator 91 | [{:keys [percent-deteriorated]}] 92 | (and (>= percent-deteriorated 0) 93 | (<= percent-deteriorated 100))) 94 | 95 | (def bobby 96 | (atom 97 | {:cuddle-hunger-level 0 :percent-deteriorated 0} 98 | :validator percent-deteriorated-validator)) 99 | (swap! bobby update-in [:percent-deteriorated] + 200) 100 | ; This throws "Invalid reference state" 101 | 102 | (defn percent-deteriorated-validator 103 | [{:keys [percent-deteriorated]}] 104 | (or (and (>= percent-deteriorated 0) 105 | (<= percent-deteriorated 100)) 106 | (throw (IllegalStateException. "That's not mathy!")))) 107 | (def bobby 108 | (atom 109 | {:cuddle-hunger-level 0 :percent-deteriorated 0} 110 | :validator percent-deteriorated-validator)) 111 | (swap! bobby update-in [:percent-deteriorated] + 200) 112 | ; This throws "IllegalStateException That's not mathy!" 113 | 114 | (def sock-varieties 115 | #{"darned" "argyle" "wool" "horsehair" "mulleted" 116 | "passive-aggressive" "striped" "polka-dotted" 117 | "athletic" "business" "power" "invisible" "gollumed"}) 118 | 119 | (defn sock-count 120 | [sock-variety count] 121 | {:variety sock-variety 122 | :count count}) 123 | 124 | (defn generate-sock-gnome 125 | "Create an initial sock gnome state with no socks" 126 | [name] 127 | {:name name 128 | :socks #{}}) 129 | 130 | (def sock-gnome (ref (generate-sock-gnome "Barumpharumph"))) 131 | (def dryer (ref {:name "LG 1337" 132 | :socks (set (map #(sock-count % 2) sock-varieties))})) 133 | 134 | (:socks @dryer) 135 | ; => #{{:variety "passive-aggressive", :count 2} {:variety "power", :count 2} 136 | ; => {:variety "athletic", :count 2} {:variety "business", :count 2} 137 | ; => {:variety "argyle", :count 2} {:variety "horsehair", :count 2} 138 | ; => {:variety "gollumed", :count 2} {:variety "darned", :count 2} 139 | ; => {:variety "polka-dotted", :count 2} {:variety "wool", :count 2} 140 | ; => {:variety "mulleted", :count 2} {:variety "striped", :count 2} 141 | ; => {:variety "invisible", :count 2}} 142 | 143 | (defn steal-sock 144 | [gnome dryer] 145 | (dosync 146 | (when-let [pair (some #(if (= (:count %) 2) %) (:socks @dryer))] 147 | (let [updated-count (sock-count (:variety pair) 1)] 148 | (alter gnome update-in [:socks] conj updated-count) 149 | (alter dryer update-in [:socks] disj pair) 150 | (alter dryer update-in [:socks] conj updated-count))))) 151 | (steal-sock sock-gnome dryer) 152 | 153 | (:socks @sock-gnome) 154 | ; => #{{:variety "passive-aggressive", :count 1}} 155 | 156 | (defn similar-socks 157 | [target-sock sock-set] 158 | (filter #(= (:variety %) (:variety target-sock)) sock-set)) 159 | 160 | (similar-socks (first (:socks @sock-gnome)) (:socks @dryer)) 161 | ; => ({:variety "passive-aggressive", :count 1}) 162 | 163 | 164 | (def counter (ref 0)) 165 | (future 166 | (dosync 167 | (alter counter inc) 168 | (println @counter) 169 | (Thread/sleep 500) 170 | (alter counter inc) 171 | (println @counter))) 172 | (Thread/sleep 250) 173 | (println @counter) 174 | 175 | (defn sleep-print-update 176 | [sleep-time thread-name update-fn] 177 | (fn [state] 178 | (Thread/sleep sleep-time) 179 | (println (str thread-name ": " state)) 180 | (update-fn state))) 181 | (def counter (ref 0)) 182 | (future (dosync (commute counter (sleep-print-update 100 "Thread A" inc)))) 183 | (future (dosync (commute counter (sleep-print-update 150 "Thread B" inc)))) 184 | 185 | (def receiver-a (ref #{})) 186 | (def receiver-b (ref #{})) 187 | (def giver (ref #{1})) 188 | (do (future (dosync (let [gift (first @giver)] 189 | (Thread/sleep 10) 190 | (commute receiver-a conj gift) 191 | (commute giver disj gift)))) 192 | (future (dosync (let [gift (first @giver)] 193 | (Thread/sleep 50) 194 | (commute receiver-b conj gift) 195 | (commute giver disj gift))))) 196 | 197 | @receiver-a 198 | ; => #{1} 199 | 200 | @receiver-b 201 | ; => #{1} 202 | 203 | @giver 204 | ; => #{} 205 | 206 | 207 | (def ^:dynamic *notification-address* "dobby@elf.org") 208 | 209 | (binding [*notification-address* "test@elf.org"] 210 | *notification-address*) 211 | ; => "test@elf.org" 212 | 213 | (binding [*notification-address* "tester-1@elf.org"] 214 | (println *notification-address*) 215 | (binding [*notification-address* "tester-2@elf.org"] 216 | (println *notification-address*)) 217 | (println *notification-address*)) 218 | ; => tester-1@elf.org 219 | ; => tester-2@elf.org 220 | ; => tester-1@elf.org 221 | 222 | (defn notify 223 | [message] 224 | (str "TO: " *notification-address* "\n" 225 | "MESSAGE: " message)) 226 | (notify "I fell.") 227 | ; => "TO: dobby@elf.org\nMESSAGE: I fell." 228 | 229 | (binding [*notification-address* "test@elf.org"] 230 | (notify "test!")) 231 | ; => "TO: test@elf.org\nMESSAGE: test!" 232 | 233 | (binding [*out* (clojure.java.io/writer "print-output")] 234 | (println "A man who carries a cat by the tail learns 235 | something he can learn in no other way. 236 | -- Mark Twain")) 237 | (slurp "print-output") 238 | ; => A man who carries a cat by the tail learns 239 | ; => something he can learn in no other way. 240 | ; => -- Mark Twain 241 | 242 | (println ["Print" "all" "the" "things!"]) 243 | ; => [Print all the things!] 244 | 245 | (binding [*print-length* 1] 246 | (println ["Print" "just" "one!"])) 247 | ; => [Print ...] 248 | 249 | (def ^:dynamic *troll-thought* nil) 250 | (defn troll-riddle 251 | [your-answer] 252 | (let [number "man meat"] 253 | (when (thread-bound? #'*troll-thought*) 254 | (set! *troll-thought* number)) 255 | (if (= number your-answer) 256 | "TROLL: You can cross the bridge!" 257 | "TROLL: Time to eat you, succulent human!"))) 258 | 259 | (binding [*troll-thought* nil] 260 | (println (troll-riddle 2)) 261 | (println "SUCCULENT HUMAN: Oooooh! The answer was" *troll-thought*)) 262 | 263 | ; => TROLL: Time to eat you, succulent human! 264 | ; => SUCCULENT HUMAN: Oooooh! The answer was man meat 265 | 266 | *troll-thought* 267 | ; => nil 268 | 269 | (.write *out* "prints to repl") 270 | ; => prints to repl 271 | 272 | (.start (Thread. #(.write *out* "prints to standard out"))) 273 | 274 | (let [out *out*] 275 | (.start 276 | (Thread. #(binding [*out* out] 277 | (.write *out* "prints to repl from thread"))))) 278 | 279 | (.start (Thread. (bound-fn [] (.write *out* "prints to repl from thread")))) 280 | 281 | (def power-source "hair") 282 | 283 | (alter-var-root #'power-source (fn [_] "7-eleven parking lot")) 284 | power-source 285 | ; => "7-eleven parking lot" 286 | 287 | (with-redefs [*out* *out*] 288 | (doto (Thread. #(println "with redefs allows me to show up in the REPL")) 289 | .start 290 | .join)) 291 | 292 | (defn always-1 293 | [] 294 | 1) 295 | (take 5 (repeatedly always-1)) 296 | ; => (1 1 1 1 1) 297 | 298 | (take 5 (repeatedly (partial rand-int 10))) 299 | ; => (1 5 0 3 4) 300 | 301 | (def alphabet-length 26) 302 | 303 | ;; Vector of chars, A-Z 304 | (def letters (mapv (comp str char (partial + 65)) (range alphabet-length))) 305 | 306 | (defn random-string 307 | "Returns a random string of specified length" 308 | [length] 309 | (apply str (take length (repeatedly #(rand-nth letters))))) 310 | 311 | (defn random-string-list 312 | [list-length string-length] 313 | (doall (take list-length (repeatedly (partial random-string string-length))))) 314 | 315 | (def orc-names (random-string-list 3000 7000)) 316 | 317 | (time (dorun (map clojure.string/lower-case orc-names))) 318 | ; => "Elapsed time: 270.182 msecs" 319 | 320 | (time (dorun (pmap clojure.string/lower-case orc-names))) 321 | ; => "Elapsed time: 147.562 msecs" 322 | 323 | 324 | (def orc-name-abbrevs (random-string-list 20000 300)) 325 | (time (dorun (map clojure.string/lower-case orc-name-abbrevs))) 326 | ; => "Elapsed time: 78.23 msecs" 327 | (time (dorun (pmap clojure.string/lower-case orc-name-abbrevs))) 328 | ; => "Elapsed time: 124.727 msecs" 329 | 330 | (def numbers [1 2 3 4 5 6 7 8 9 10]) 331 | (partition-all 3 numbers) 332 | ; => ((1 2 3) (4 5 6) (7 8 9) (10)) 333 | 334 | (pmap inc numbers) 335 | 336 | (pmap (fn [number-group] (doall (map inc number-group))) 337 | (partition-all 3 numbers)) 338 | ; => ((2 3 4) (5 6 7) (8 9 10) (11)) 339 | 340 | (apply concat 341 | (pmap (fn [number-group] (doall (map inc number-group))) 342 | (partition-all 3 numbers))) 343 | 344 | (time 345 | (dorun 346 | (apply concat 347 | (pmap (fn [name] (doall (map clojure.string/lower-case name))) 348 | (partition-all 1000 orc-name-abbrevs))))) 349 | ; => "Elapsed time: 44.677 msecs" 350 | 351 | (defn ppmap 352 | "Partitioned pmap, for grouping map ops together to make parallel 353 | overhead worthwhile" 354 | [grain-size f & colls] 355 | (apply concat 356 | (apply pmap 357 | (fn [& pgroups] (doall (apply map f pgroups))) 358 | (map (partial partition-all grain-size) colls)))) 359 | (time (dorun (ppmap 1000 clojure.string/lower-case orc-name-abbrevs))) 360 | ; => "Elapsed time: 44.902 msecs" 361 | 362 | (quote-word-count 5) 363 | ; => {"ochre" 8, "smoothie" 2} 364 | -------------------------------------------------------------------------------- /10/code.rb: -------------------------------------------------------------------------------- 1 | class CuddleZombie 2 | # attr_accessor is just a shorthand way for creating getters and 3 | # setters for the listed instance variables 4 | attr_accessor :cuddle_hunger_level, :percent_deteriorated 5 | 6 | def initialize(cuddle_hunger_level = 1, percent_deteriorated = 0) 7 | self.cuddle_hunger_level = cuddle_hunger_level 8 | self.percent_deteriorated = percent_deteriorated 9 | end 10 | end 11 | 12 | fred = CuddleZombie.new(2, 3) 13 | fred.cuddle_hunger_level # => 2 14 | fred.percent_deteriorated # => 3 15 | 16 | fred.cuddle_hunger_level = 3 17 | fred.cuddle_hunger_level # => 3 18 | 19 | 20 | if fred.percent_deteriorated >= 50 21 | Thread.new { database_logger.log(fred.cuddle_hunger_level) } 22 | end 23 | 24 | 25 | fred.cuddle_hunger_level = fred.cuddle_hunger_level + 1 26 | # At this time, another thread could read fred's attributes and 27 | # "perceive" fred in an inconsistent state unless you use a mutex 28 | fred.percent_deteriorated = fred.percent_deteriorated + 1 29 | -------------------------------------------------------------------------------- /11/README.md: -------------------------------------------------------------------------------- 1 | # Chapter 11: Mastering Concurrent Processes with core.async 2 | 3 | [`playsync`](playsync) contains all the functions and definitions 4 | listed in the chapter, and [`code.clj`](code) has the examples of the 5 | functions being used, as well as other snippets. The `playsync` 6 | project is a little funky because it's not really a project. It just 7 | makes it more convenient to use core.async functions, and it does this 8 | by putting the functions in namespace that refers the core.async 9 | functions. 10 | -------------------------------------------------------------------------------- /11/code.clj: -------------------------------------------------------------------------------- 1 | ;; blocks indefinitely 2 | (>!! (chan) "mustard") 3 | 4 | 5 | (def echo-buffer (chan 2)) 6 | (>!! echo-buffer "ketchup") 7 | ; => true 8 | (>!! echo-buffer "ketchup") 9 | ; => true 10 | (>!! echo-buffer "ketchup") 11 | ; This blocks because the channel buffer is full 12 | 13 | 14 | (def hi-chan (chan)) 15 | (doseq [n (range 1000)] 16 | (go (>! hi-chan (str "hi " n)))) 17 | 18 | (thread (println (!! echo-chan "mustard") 20 | ; => true 21 | ; => mustard 22 | 23 | (let [t (thread "chili")] 24 | ( "chili" 26 | 27 | 28 | 29 | ;; Hot dog machine stuff 30 | 31 | (let [[in out] (hot-dog-machine)] 32 | (>!! in "pocket lint") 33 | ( "hot dog" 35 | 36 | ;; Hot dog machine v2 37 | (let [[in out] (hot-dog-machine-v2 2)] 38 | (>!! in "pocket lint") 39 | (println (!! in 3) 42 | (println (!! in 3) 45 | (println (!! in 3) 48 | ( wilted lettuce 50 | ; => hotdog 51 | ; => hotdog 52 | ; => nil 53 | 54 | 55 | ;; Pipeline 56 | (let [c1 (chan) 57 | c2 (chan) 58 | c3 (chan)] 59 | (go (>! c2 (clojure.string/upper-case (! c3 (clojure.string/reverse (!! c1 "redrum")) 63 | ; => MURDER 64 | 65 | 66 | ;; alts!! 67 | (let [c1 (chan) 68 | c2 (chan) 69 | c3 (chan)] 70 | (upload "serious.jpg" c1) 71 | (upload "fun.jpg" c2) 72 | (upload "sassy.jpg" c3) 73 | (let [[headshot channel] (alts!! [c1 c2 c3])] 74 | (println "Sending headshot notification for" headshot))) 75 | ; => Sending headshot notification for sassy.jpg 76 | 77 | (let [c1 (chan)] 78 | (upload "serious.jpg" c1) 79 | (let [[headshot channel] (alts!! [c1 (timeout 20)])] 80 | (if headshot 81 | (println "Sending headshot notification for" headshot) 82 | (println "Timed out!")))) 83 | ; => Timed out! 84 | 85 | (let [c1 (chan) 86 | c2 (chan)] 87 | (go ( true 92 | ; => true 93 | 94 | ;; reverser 95 | (printer reverser-out) 96 | 97 | (>!! in-chan "redrum") 98 | ; => MURDER 99 | 100 | (>!! in-chan "repaid") 101 | ; => DIAPER 102 | -------------------------------------------------------------------------------- /11/playsync/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | .hgignore 11 | .hg/ 12 | -------------------------------------------------------------------------------- /11/playsync/LICENSE: -------------------------------------------------------------------------------- 1 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 2 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 3 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 4 | 5 | 1. DEFINITIONS 6 | 7 | "Contribution" means: 8 | 9 | a) in the case of the initial Contributor, the initial code and 10 | documentation distributed under this Agreement, and 11 | 12 | b) in the case of each subsequent Contributor: 13 | 14 | i) changes to the Program, and 15 | 16 | ii) additions to the Program; 17 | 18 | where such changes and/or additions to the Program originate from and are 19 | distributed by that particular Contributor. A Contribution 'originates' from 20 | a Contributor if it was added to the Program by such Contributor itself or 21 | anyone acting on such Contributor's behalf. Contributions do not include 22 | additions to the Program which: (i) are separate modules of software 23 | distributed in conjunction with the Program under their own license 24 | agreement, and (ii) are not derivative works of the Program. 25 | 26 | "Contributor" means any person or entity that distributes the Program. 27 | 28 | "Licensed Patents" mean patent claims licensable by a Contributor which are 29 | necessarily infringed by the use or sale of its Contribution alone or when 30 | combined with the Program. 31 | 32 | "Program" means the Contributions distributed in accordance with this 33 | Agreement. 34 | 35 | "Recipient" means anyone who receives the Program under this Agreement, 36 | including all Contributors. 37 | 38 | 2. GRANT OF RIGHTS 39 | 40 | a) Subject to the terms of this Agreement, each Contributor hereby grants 41 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 42 | reproduce, prepare derivative works of, publicly display, publicly perform, 43 | distribute and sublicense the Contribution of such Contributor, if any, and 44 | such derivative works, in source code and object code form. 45 | 46 | b) Subject to the terms of this Agreement, each Contributor hereby grants 47 | Recipient a non-exclusive, worldwide, royalty-free patent license under 48 | Licensed Patents to make, use, sell, offer to sell, import and otherwise 49 | transfer the Contribution of such Contributor, if any, in source code and 50 | object code form. This patent license shall apply to the combination of the 51 | Contribution and the Program if, at the time the Contribution is added by the 52 | Contributor, such addition of the Contribution causes such combination to be 53 | covered by the Licensed Patents. The patent license shall not apply to any 54 | other combinations which include the Contribution. No hardware per se is 55 | licensed hereunder. 56 | 57 | c) Recipient understands that although each Contributor grants the licenses 58 | to its Contributions set forth herein, no assurances are provided by any 59 | Contributor that the Program does not infringe the patent or other 60 | intellectual property rights of any other entity. Each Contributor disclaims 61 | any liability to Recipient for claims brought by any other entity based on 62 | infringement of intellectual property rights or otherwise. As a condition to 63 | exercising the rights and licenses granted hereunder, each Recipient hereby 64 | assumes sole responsibility to secure any other intellectual property rights 65 | needed, if any. For example, if a third party patent license is required to 66 | allow Recipient to distribute the Program, it is Recipient's responsibility 67 | to acquire that license before distributing the Program. 68 | 69 | d) Each Contributor represents that to its knowledge it has sufficient 70 | copyright rights in its Contribution, if any, to grant the copyright license 71 | set forth in this Agreement. 72 | 73 | 3. REQUIREMENTS 74 | 75 | A Contributor may choose to distribute the Program in object code form under 76 | its own license agreement, provided that: 77 | 78 | a) it complies with the terms and conditions of this Agreement; and 79 | 80 | b) its license agreement: 81 | 82 | i) effectively disclaims on behalf of all Contributors all warranties and 83 | conditions, express and implied, including warranties or conditions of title 84 | and non-infringement, and implied warranties or conditions of merchantability 85 | and fitness for a particular purpose; 86 | 87 | ii) effectively excludes on behalf of all Contributors all liability for 88 | damages, including direct, indirect, special, incidental and consequential 89 | damages, such as lost profits; 90 | 91 | iii) states that any provisions which differ from this Agreement are offered 92 | by that Contributor alone and not by any other party; and 93 | 94 | iv) states that source code for the Program is available from such 95 | Contributor, and informs licensees how to obtain it in a reasonable manner on 96 | or through a medium customarily used for software exchange. 97 | 98 | When the Program is made available in source code form: 99 | 100 | a) it must be made available under this Agreement; and 101 | 102 | b) a copy of this Agreement must be included with each copy of the Program. 103 | 104 | Contributors may not remove or alter any copyright notices contained within 105 | the Program. 106 | 107 | Each Contributor must identify itself as the originator of its Contribution, 108 | if any, in a manner that reasonably allows subsequent Recipients to identify 109 | the originator of the Contribution. 110 | 111 | 4. COMMERCIAL DISTRIBUTION 112 | 113 | Commercial distributors of software may accept certain responsibilities with 114 | respect to end users, business partners and the like. While this license is 115 | intended to facilitate the commercial use of the Program, the Contributor who 116 | includes the Program in a commercial product offering should do so in a 117 | manner which does not create potential liability for other Contributors. 118 | Therefore, if a Contributor includes the Program in a commercial product 119 | offering, such Contributor ("Commercial Contributor") hereby agrees to defend 120 | and indemnify every other Contributor ("Indemnified Contributor") against any 121 | losses, damages and costs (collectively "Losses") arising from claims, 122 | lawsuits and other legal actions brought by a third party against the 123 | Indemnified Contributor to the extent caused by the acts or omissions of such 124 | Commercial Contributor in connection with its distribution of the Program in 125 | a commercial product offering. The obligations in this section do not apply 126 | to any claims or Losses relating to any actual or alleged intellectual 127 | property infringement. In order to qualify, an Indemnified Contributor must: 128 | a) promptly notify the Commercial Contributor in writing of such claim, and 129 | b) allow the Commercial Contributor tocontrol, and cooperate with the 130 | Commercial Contributor in, the defense and any related settlement 131 | negotiations. The Indemnified Contributor may participate in any such claim 132 | at its own expense. 133 | 134 | For example, a Contributor might include the Program in a commercial product 135 | offering, Product X. That Contributor is then a Commercial Contributor. If 136 | that Commercial Contributor then makes performance claims, or offers 137 | warranties related to Product X, those performance claims and warranties are 138 | such Commercial Contributor's responsibility alone. Under this section, the 139 | Commercial Contributor would have to defend claims against the other 140 | Contributors related to those performance claims and warranties, and if a 141 | court requires any other Contributor to pay any damages as a result, the 142 | Commercial Contributor must pay those damages. 143 | 144 | 5. NO WARRANTY 145 | 146 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON 147 | AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER 148 | EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR 149 | CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A 150 | PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the 151 | appropriateness of using and distributing the Program and assumes all risks 152 | associated with its exercise of rights under this Agreement , including but 153 | not limited to the risks and costs of program errors, compliance with 154 | applicable laws, damage to or loss of data, programs or equipment, and 155 | unavailability or interruption of operations. 156 | 157 | 6. DISCLAIMER OF LIABILITY 158 | 159 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 160 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 161 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION 162 | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 163 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 164 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 165 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY 166 | OF SUCH DAMAGES. 167 | 168 | 7. GENERAL 169 | 170 | If any provision of this Agreement is invalid or unenforceable under 171 | applicable law, it shall not affect the validity or enforceability of the 172 | remainder of the terms of this Agreement, and without further action by the 173 | parties hereto, such provision shall be reformed to the minimum extent 174 | necessary to make such provision valid and enforceable. 175 | 176 | If Recipient institutes patent litigation against any entity (including a 177 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 178 | (excluding combinations of the Program with other software or hardware) 179 | infringes such Recipient's patent(s), then such Recipient's rights granted 180 | under Section 2(b) shall terminate as of the date such litigation is filed. 181 | 182 | All Recipient's rights under this Agreement shall terminate if it fails to 183 | comply with any of the material terms or conditions of this Agreement and 184 | does not cure such failure in a reasonable period of time after becoming 185 | aware of such noncompliance. If all Recipient's rights under this Agreement 186 | terminate, Recipient agrees to cease use and distribution of the Program as 187 | soon as reasonably practicable. However, Recipient's obligations under this 188 | Agreement and any licenses granted by Recipient relating to the Program shall 189 | continue and survive. 190 | 191 | Everyone is permitted to copy and distribute copies of this Agreement, but in 192 | order to avoid inconsistency the Agreement is copyrighted and may only be 193 | modified in the following manner. The Agreement Steward reserves the right to 194 | publish new versions (including revisions) of this Agreement from time to 195 | time. No one other than the Agreement Steward has the right to modify this 196 | Agreement. The Eclipse Foundation is the initial Agreement Steward. The 197 | Eclipse Foundation may assign the responsibility to serve as the Agreement 198 | Steward to a suitable separate entity. Each new version of the Agreement will 199 | be given a distinguishing version number. The Program (including 200 | Contributions) may always be distributed subject to the version of the 201 | Agreement under which it was received. In addition, after a new version of 202 | the Agreement is published, Contributor may elect to distribute the Program 203 | (including its Contributions) under the new version. Except as expressly 204 | stated in Sections 2(a) and 2(b) above, Recipient receives no rights or 205 | licenses to the intellectual property of any Contributor under this 206 | Agreement, whether expressly, by implication, estoppel or otherwise. All 207 | rights in the Program not expressly granted under this Agreement are 208 | reserved. 209 | 210 | This Agreement is governed by the laws of the State of New York and the 211 | intellectual property laws of the United States of America. No party to this 212 | Agreement will bring a legal action under this Agreement more than one year 213 | after the cause of action arose. Each party waives its rights to a jury trial 214 | in any resulting litigation. 215 | -------------------------------------------------------------------------------- /11/playsync/README.md: -------------------------------------------------------------------------------- 1 | # playsync 2 | 3 | FIXME: description 4 | 5 | ## Installation 6 | 7 | Download from http://example.com/FIXME. 8 | 9 | ## Usage 10 | 11 | FIXME: explanation 12 | 13 | $ java -jar playsync-0.1.0-standalone.jar [args] 14 | 15 | ## Options 16 | 17 | FIXME: listing of options this app accepts. 18 | 19 | ## Examples 20 | 21 | ... 22 | 23 | ### Bugs 24 | 25 | ... 26 | 27 | ### Any Other Sections 28 | ### That You Think 29 | ### Might be Useful 30 | 31 | ## License 32 | 33 | Copyright © 2015 FIXME 34 | 35 | Distributed under the Eclipse Public License either version 1.0 or (at 36 | your option) any later version. 37 | -------------------------------------------------------------------------------- /11/playsync/doc/intro.md: -------------------------------------------------------------------------------- 1 | # Introduction to playsync 2 | 3 | TODO: write [great documentation](http://jacobian.org/writing/what-to-write/) 4 | -------------------------------------------------------------------------------- /11/playsync/project.clj: -------------------------------------------------------------------------------- 1 | (defproject playsync "0.1.0-SNAPSHOT" 2 | :description "FIXME: write description" 3 | :url "http://example.com/FIXME" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :dependencies [[org.clojure/clojure "1.7.0"] 7 | [org.clojure/core.async "0.1.346.0-17112a-alpha"]] 8 | :main ^:skip-aot playsync.core 9 | :target-path "target/%s" 10 | :profiles {:uberjar {:aot :all}}) 11 | -------------------------------------------------------------------------------- /11/playsync/src/playsync/core.clj: -------------------------------------------------------------------------------- 1 | (ns playsync.core 2 | (:require [clojure.core.async 3 | :as a 4 | :refer [>! !! !! echo-chan "ketchup") 10 | 11 | (defn hot-dog-machine 12 | [] 13 | (let [in (chan) 14 | out (chan)] 15 | (go (! out "hot dog")) 17 | [in out])) 18 | 19 | (defn hot-dog-machine-v2 20 | [hot-dog-count] 21 | (let [in (chan) 22 | out (chan)] 23 | (go (loop [hc hot-dog-count] 24 | (if (> hc 0) 25 | (let [input (! out "hot dog") 28 | (recur (dec hc))) 29 | (do (>! out "wilted lettuce") 30 | (recur hc)))) 31 | (do (close! in) 32 | (close! out))))) 33 | [in out])) 34 | 35 | (defn upload 36 | [headshot c] 37 | (go (Thread/sleep (rand 100)) 38 | (>! c headshot))) 39 | 40 | 41 | (defn append-to-file 42 | "Write a string to the end of a file" 43 | [filename s] 44 | (spit filename s :append true)) 45 | 46 | (defn format-quote 47 | "Delineate the beginning and end of a quote because it's convenient" 48 | [quote] 49 | (str "=== BEGIN QUOTE ===\n" quote "=== END QUOTE ===\n\n")) 50 | 51 | (defn random-quote 52 | "Retrieve a random quote and format it" 53 | [] 54 | (format-quote (slurp "http://www.braveclojure.com/random-quote"))) 55 | 56 | (defn snag-quotes 57 | [filename num-quotes] 58 | (let [c (chan)] 59 | (go (while true (append-to-file filename (! c (random-quote)))))) 61 | 62 | (defn upper-caser 63 | [in] 64 | (let [out (chan)] 65 | (go (while true (>! out (clojure.string/upper-case (! out (clojure.string/reverse ( "BY BLUEBEARD'S BANANAS!" 3 | 4 | (.indexOf "Let's synergize our bleeding edges" "y") 5 | ; => 7 6 | 7 | (macroexpand-1 '(.toUpperCase "By Bluebeard's bananas!")) 8 | ; => (. "By Bluebeard's bananas!" toUpperCase) 9 | 10 | (macroexpand-1 '(.indexOf "Let's synergize our bleeding edges" "y")) 11 | ; => (. "Let's synergize our bleeding edges" indexOf "y") 12 | 13 | (macroexpand-1 '(Math/abs -3)) 14 | ; => (. Math abs -3) 15 | 16 | (new String) 17 | ; => "" 18 | 19 | (String.) 20 | ; => "" 21 | 22 | (String. "To Davey Jones's Locker with ye hardies") 23 | ; => "To Davey Jones's Locker with ye hardies" 24 | 25 | (java.util.Stack.) 26 | ; => [] 27 | 28 | (let [stack (java.util.Stack.)] 29 | (.push stack "Latest episode of Game of Thrones, ho!") 30 | stack) 31 | ; => ["Latest episode of Game of Thrones, ho!"] 32 | 33 | 34 | (let [stack (java.util.Stack.)] 35 | (.push stack "Latest episode of Game of Thrones, ho!") 36 | (first stack)) 37 | ; => "Latest episode of Game of Thrones, ho!" 38 | 39 | 40 | (doto (java.util.Stack.) 41 | (.push "Latest episode of Game of Thrones, ho!") 42 | (.push "Whoops, I meant 'Land, ho!'")) 43 | ; => ["Latest episode of Game of Thrones, ho!" "Whoops, I meant 'Land, ho!'"] 44 | 45 | 46 | (macroexpand-1 47 | '(doto (java.util.Stack.) 48 | (.push "Latest episode of Game of Thrones, ho!") 49 | (.push "Whoops, I meant 'Land, ho!'"))) 50 | ; => (clojure.core/let 51 | ; => [G__2876 (java.util.Stack.)] 52 | ; => (.push G__2876 "Latest episode of Game of Thrones, ho!") 53 | ; => (.push G__2876 "Whoops, I meant 'Land, ho!'") 54 | ; => G__2876) 55 | 56 | (import java.util.Stack) 57 | (Stack.) 58 | ; => [] 59 | 60 | (import [java.util Date Stack] 61 | [java.net Proxy URI]) 62 | 63 | (Date.) 64 | ; => #inst "2016-09-19T20:40:02.733-00:00" 65 | 66 | (ns pirate.talk 67 | (:import [java.util Date Stack] 68 | [java.net Proxy URI])) 69 | 70 | (System/getenv) 71 | {"USER" "the-incredible-bulk" 72 | "JAVA_ARCH" "x86_64"} 73 | 74 | (System/getProperty "user.dir") 75 | ; => "/Users/dabulk/projects/dabook" 76 | 77 | (System/getProperty "java.version") 78 | ; => "1.7.0_17" 79 | 80 | #inst "2016-09-19T20:40:02.733-00:00" 81 | 82 | (let [file (java.io.File. "/")] 83 | (println (.exists file)) 84 | (println (.canWrite file)) 85 | (println (.getPath file))) 86 | ; => true 87 | ; => false 88 | ; => / 89 | 90 | (spit "/tmp/hercules-todo-list" 91 | "- kill dat lion brov 92 | - chop up what nasty multi-headed snake thing") 93 | 94 | (slurp "/tmp/hercules-todo-list") 95 | 96 | ; => "- kill dat lion brov 97 | ; => - chop up what nasty multi-headed snake thing" 98 | 99 | (let [s (java.io.StringWriter.)] 100 | (spit s "- capture cerynian hind like for real") 101 | (.toString s)) 102 | ; => "- capture cerynian hind like for real" 103 | 104 | 105 | (let [s (java.io.StringReader. "- get erymanthian pig what with the tusks")] 106 | (slurp s)) 107 | ; => "- get erymanthian pig what with the tusks" 108 | 109 | 110 | (with-open [todo-list-rdr (clojure.java.io/reader "/tmp/hercules-todo-list")] 111 | (println (first (line-seq todo-list-rdr)))) 112 | ; => - kill dat lion brov 113 | -------------------------------------------------------------------------------- /12/code.java: -------------------------------------------------------------------------------- 1 | ScaryClown bellyRubsTheClown = new ScaryClown(); 2 | bellyRubsTheClown.balloonCount(); 3 | // => 0 4 | 5 | bellyRubsTheClown.receiveBalloons(2); 6 | bellyRubsTheClown.balloonCount(); 7 | // => 2 8 | 9 | bellyRubsTheClown.makeBalloonArt(); 10 | // => "Belly Rubs makes a balloon shaped like a clown, because Belly Rubs 11 | // => is trying to scare you and nothing is scarier than clowns." 12 | 13 | Math.abs(-50); 14 | 15 | "By Bluebeard's bananas!".toUpperCase(); 16 | "Let's synergize our bleeding edges".indexOf("y"); 17 | -------------------------------------------------------------------------------- /12/phrasebook/PirateConversation.java: -------------------------------------------------------------------------------- 1 | import pirate_phrases.*; 2 | 3 | public class PirateConversation 4 | { 5 | public static void main(String[] args) 6 | { 7 | Greetings greetings = new Greetings(); 8 | greetings.hello(); 9 | 10 | Farewells farewells = new Farewells(); 11 | farewells.goodbye(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /12/phrasebook/PiratePhrases.java: -------------------------------------------------------------------------------- 1 | public class PiratePhrases 2 | { 3 | public static void main(String[] args) 4 | { 5 | System.out.println("Shiver me timbers!!!"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /12/phrasebook/pirate_phrases/Farewells.java: -------------------------------------------------------------------------------- 1 | package pirate_phrases; 2 | 3 | public class Farewells 4 | { 5 | public static void goodbye() 6 | { 7 | System.out.println("A fair turn of the tide ter ye thar, ye magnificent sea friend!!"); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /12/phrasebook/pirate_phrases/Greetings.java: -------------------------------------------------------------------------------- 1 | package pirate_phrases; 2 | 3 | public class Greetings 4 | { 5 | public static void hello() 6 | { 7 | System.out.println("Shiver me timbers!!!"); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /13/README.md: -------------------------------------------------------------------------------- 1 | # Chapter 13: Creating and Extending Abstractions with Multimethods, Protocols, and Records 2 | 3 | [`code.clj`](code.clj) has all the Clojure code from the chapter. 4 | -------------------------------------------------------------------------------- /13/code.clj: -------------------------------------------------------------------------------- 1 | (ns were-creatures) 2 | 3 | (defmulti full-moon-behavior (fn [were-creature] (:were-type were-creature))) 4 | 5 | (defmethod full-moon-behavior :wolf 6 | [were-creature] 7 | (str (:name were-creature) " will howl and murder")) 8 | 9 | (defmethod full-moon-behavior :simmons 10 | [were-creature] 11 | (str (:name were-creature) " will encourage people and sweat to the oldies")) 12 | 13 | (full-moon-behavior {:were-type :wolf 14 | :name "Rachel from next door"}) 15 | ; => "Rachel from next door will howl and murder" 16 | 17 | (full-moon-behavior {:name "Andy the baker" 18 | :were-type :simmons}) 19 | ; => "Andy the baker will encourage people and sweat to the oldies" 20 | 21 | (defmethod full-moon-behavior nil 22 | [were-creature] 23 | (str (:name were-creature) " will stay at home and eat ice cream")) 24 | 25 | (full-moon-behavior {:were-type nil 26 | :name "Martin the nurse"}) 27 | ; => "Martin the nurse will stay at home and eat ice cream" 28 | 29 | (defmethod full-moon-behavior :default 30 | [were-creature] 31 | (str (:name were-creature) " will stay up all night fantasy footballing")) 32 | 33 | (full-moon-behavior {:were-type :office-worker 34 | :name "Jimmy from sales"}) 35 | ; => "Jimmy from sales will stay up all night fantasy footballing" 36 | 37 | 38 | (ns random-namespace 39 | (:require [were-creatures])) 40 | (defmethod were-creatures/full-moon-behavior :bill-murray 41 | [were-creature] 42 | (str (:name were-creature) " will be the most likeable celebrity")) 43 | (were-creatures/full-moon-behavior {:name "Laura the intern" 44 | :were-type :bill-murray}) 45 | ; => "Laura the intern will be the most likeable celebrity" 46 | 47 | (ns user) 48 | (defmulti types (fn [x y] [(class x) (class y)])) 49 | (defmethod types [java.lang.String java.lang.String] 50 | [x y] 51 | "Two strings!") 52 | 53 | (types "String 1" "String 2") 54 | ; => "Two strings!" 55 | 56 | (ns data-psychology) 57 | (defprotocol Psychodynamics 58 | "Plumb the inner depths of your data types" 59 | (thoughts [x] "The data type's innermost thoughts") 60 | (feelings-about [x] [x y] "Feelings about self or other")) 61 | 62 | (feelings-about [x] [x & others]) 63 | 64 | (extend-type java.lang.String 65 | Psychodynamics 66 | (thoughts [x] (str x " thinks, 'Truly, the character defines the data type'") 67 | (feelings-about 68 | ([x] (str x " is longing for a simpler way of life")) 69 | ([x y] (str x " is envious of " y "'s simpler way of life"))))) 70 | 71 | (thoughts "blorb") 72 | ; => "blorb thinks, 'Truly, the character defines the data type'" 73 | 74 | (feelings-about "schmorb") 75 | ; => "schmorb is longing for a simpler way of life" 76 | 77 | (feelings-about "schmorb" 2) 78 | ; => "schmorb is envious of 2's simpler way of life" 79 | 80 | (extend-type java.lang.Object 81 | Psychodynamics 82 | (thoughts [x] "Maybe the Internet is just a vector for toxoplasmosis") 83 | (feelings-about 84 | ([x] "meh") 85 | ([x y] (str "meh about " y)))) 86 | 87 | (thoughts 3) 88 | ; => "Maybe the Internet is just a vector for toxoplasmosis" 89 | 90 | (feelings-about 3) 91 | ; => "meh" 92 | 93 | (feelings-about 3 "blorb") 94 | ; => "meh about blorb" 95 | 96 | (extend-protocol Psychodynamics 97 | java.lang.String 98 | (thoughts [x] "Truly, the character defines the data type") 99 | (feelings-about 100 | ([x] "longing for a simpler way of life") 101 | ([x y] (str "envious of " y "'s simpler way of life"))) 102 | 103 | java.lang.Object 104 | (thoughts [x] "Maybe the Internet is just a vector for toxoplasmosis") 105 | (feelings-about 106 | ([x] "meh") 107 | ([x y] (str "meh about " y)))) 108 | 109 | (ns were-records) 110 | (defrecord WereWolf [name title]) 111 | 112 | (WereWolf. "David" "London Tourist") 113 | ; => #were_records.WereWolf{:name "David", :title "London Tourist"} 114 | 115 | (->WereWolf "Jacob" "Lead Shirt Discarder") 116 | ; => #were_records.WereWolf{:name "Jacob", :title "Lead Shirt Discarder"} 117 | 118 | (map->WereWolf {:name "Lucian" :title "CEO of Melodrama"}) 119 | ; => #were_records.WereWolf{:name "Lucian", :title "CEO of Melodrama"} 120 | 121 | (ns monster-mash 122 | (:import [were_records WereWolf])) 123 | (WereWolf. "David" "London Tourist") 124 | ; => #were_records.WereWolf{:name "David", :title "London Tourist"} 125 | 126 | (def jacob (->WereWolf "Jacob" "Lead Shirt Discarder")) 127 | (.name jacob) 128 | ; => "Jacob" 129 | 130 | (:name jacob) 131 | ; => "Jacob" 132 | 133 | (get jacob :name) 134 | ; => "Jacob" 135 | 136 | (= jacob (->WereWolf "Jacob" "Lead Shirt Discarder")) 137 | ; => true 138 | 139 | (= jacob (WereWolf. "David" "London Tourist")) 140 | ; => false 141 | 142 | (= jacob {:name "Jacob" :title "Lead Shirt Discarder"}) 143 | ; => false 144 | 145 | (assoc jacob :title "Lead Third Wheel") 146 | ; => #were_records.WereWolf{:name "Jacob", :title "Lead Third Wheel"} 147 | 148 | (dissoc jacob :title) 149 | ; => {:name "Jacob"} <- that's not a were_records.WereWolf 150 | 151 | 152 | (defprotocol WereCreature 153 | (full-moon-behavior [x])) 154 | 155 | (defrecord WereWolf [name title] 156 | WereCreature 157 | (full-moon-behavior [x] 158 | (str name " will howl and murder"))) 159 | 160 | (full-moon-behavior (map->WereWolf {:name "Lucian" :title "CEO of Melodrama"})) 161 | ; => "Lucian will howl and murder" 162 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Clojure for the Brave and True Code 2 | 3 | This contains all the code examples from the book Clojure for the 4 | Brave and True, which you can read at http://braveclojure.com. Also check 5 | out the print version at https://nostarch.com/clojure! 6 | 7 | This repo is organized by chapter, with the `01` directory corresponding to 8 | chapter 1, `02` chapter 2 - you get the idea. Each directory has its own 9 | README with additional notes. 10 | 11 | Have fun! 12 | --------------------------------------------------------------------------------