├── test └── practicalli │ └── getting_started_test.clj ├── deps.edn ├── README.md ├── pom.xml └── src └── practicalli ├── data_structures.clj ├── getting_started.clj ├── destructuring_tictactoe.clj ├── functions.clj └── exercise_shires_next_top_model.clj /test/practicalli/getting_started_test.clj: -------------------------------------------------------------------------------- 1 | (ns practicalli.getting-started-test 2 | (:require [clojure.test :refer :all] 3 | [practicalli.getting-started :refer :all])) 4 | 5 | (deftest a-test 6 | (testing "FIXME, I fail." 7 | (is (= 0 1)))) 8 | -------------------------------------------------------------------------------- /deps.edn: -------------------------------------------------------------------------------- 1 | {:paths 2 | ["src" "resources"] 3 | 4 | :deps 5 | {org.clojure/clojure {:mvn/version "1.10.1"}} 6 | 7 | :aliases 8 | { 9 | :test 10 | {:extra-paths ["test"] 11 | :extra-deps {org.clojure/test.check {:mvn/version "1.0.0"}}} 12 | 13 | :runner 14 | {:extra-deps {com.cognitect/test-runner 15 | {:git/url "https://github.com/cognitect-labs/test-runner" 16 | :sha "b6b3193fcc42659d7e46ecd1884a228993441182"}} 17 | :main-opts ["-m" "cognitect.test-runner" 18 | "-d" "test"]} 19 | 20 | :uberjar 21 | {:extra-deps {seancorfield/depstar {:mvn/version "1.1.128"}} 22 | :main-opts ["-m" "hf.depstar.uberjar" "getting-started.jar" 23 | "-C" "-m" "practicalli.getting-started"]}}} 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # getting-started 2 | 3 | FIXME: my new application. 4 | 5 | ## Installation 6 | 7 | Download from https://github.com/practicalli/getting-started 8 | 9 | ## Usage 10 | 11 | FIXME: explanation 12 | 13 | Run the project directly: 14 | 15 | $ clojure -M -m practicalli.getting-started 16 | 17 | Run the project's tests (they'll fail until you edit them): 18 | 19 | $ clojure -M:test:runner 20 | 21 | Build an uberjar: 22 | 23 | $ clojure -M:uberjar 24 | 25 | Run that uberjar: 26 | 27 | $ java -jar getting-started.jar 28 | 29 | ## Options 30 | 31 | FIXME: listing of options this app accepts. 32 | 33 | ## Examples 34 | 35 | ... 36 | 37 | ### Bugs 38 | 39 | ... 40 | 41 | ### Any Other Sections 42 | ### That You Think 43 | ### Might be Useful 44 | 45 | ## License 46 | 47 | Copyright © 2021 Practicalli 48 | 49 | Distributed under the Eclipse Public License either version 1.0 or (at 50 | your option) any later version. 51 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | practicalli 5 | getting-started 6 | 0.1.0-SNAPSHOT 7 | practicalli/getting-started 8 | FIXME: my new application. 9 | https://github.com/practicalli/getting-started 10 | 11 | 12 | Eclipse Public License 13 | http://www.eclipse.org/legal/epl-v10.html 14 | 15 | 16 | 17 | 18 | Practicalli 19 | 20 | 21 | 22 | https://github.com/practicalli/getting-started 23 | scm:git:git://github.com/practicalli/getting-started.git 24 | scm:git:ssh://git@github.com/practicalli/getting-started.git 25 | HEAD 26 | 27 | 28 | 29 | org.clojure 30 | clojure 31 | 1.10.1 32 | 33 | 34 | 35 | src 36 | 37 | 38 | 39 | clojars 40 | https://repo.clojars.org/ 41 | 42 | 43 | sonatype 44 | https://oss.sonatype.org/content/repositories/snapshots/ 45 | 46 | 47 | 48 | 49 | clojars 50 | Clojars repository 51 | https://clojars.org/repo 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /src/practicalli/data_structures.clj: -------------------------------------------------------------------------------- 1 | (ns practicalli.data-structures) 2 | 3 | 4 | ;; Data structures 5 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 6 | 7 | ;; TODO: built in data structures 8 | ;; TODO: Immutability 9 | ;; TODO: Structural sharing - memory efficient 'copies' 10 | 11 | 12 | ;; Number types 13 | ;; https://clojure.org/reference/data_structures#Numbers 14 | 15 | ;; Related functions 16 | ;; https://clojure.org/reference/data_structures#_related_functions 17 | 18 | ;; Ratio type - lazy precision 19 | 20 | (/ 22 7) 21 | 22 | (* (/ 22 7) 9) 23 | 24 | 1 25 | 198/7 26 | 27 | ;; Strings / Characters 28 | ;; - example reverse string 29 | ;; - apply str vs clojure.string/... 30 | 31 | (reverse "racecar") 32 | ;; => (\r \a \c \e \c \a \r) 33 | 34 | (clojure.string/reverse "hello") 35 | 36 | (clojure.string/join '(\r \a \c \e \c \a \r)) 37 | (apply str (reverse "racecar")) 38 | 39 | ;; Keywords - leave until hash-maps 40 | 41 | ;; Keywords are functions with a data structure as an argument 42 | ;; data structures are functions with keys as arguments 43 | 44 | ;; Never seen defstruct used anywhere 45 | ;; Records are infrequently used 46 | 47 | 48 | ;; list data structure 49 | (quote (1 2 3)) 50 | '(1 2 3) 51 | 52 | ;; vector data structure 53 | ;; - the most common data structure (not counting lists used as function calls) 54 | ;; - an indexed collection of values 55 | 56 | (get [1 2 3] 1) 57 | 58 | (= [1 2 3] '(1 2 3 4)) 59 | 60 | (get ["a" {:name "Pugsley Winterbottom"} "c"] 1) 61 | ;; => {:name "Pugsley Winterbottom"} 62 | 63 | (["a" {:name "Pugsley Winterbottom"} "c"] 1) 64 | ;; => {:name "Pugsley Winterbottom"} 65 | 66 | ;; Contains vs some 67 | 68 | ;; looking up via a key for the data structure 69 | (contains? ["a" {:name "Pugsley Winterbottom"} "c"] "a") 70 | 71 | ;; looking up via a predicate value 72 | (some #{"a"} ["a" {:name "Pugsley Winterbottom"} "c"]) 73 | 74 | 75 | (some #{"b"} ["a" {:name "Pugsley Winterbottom"} "c"]) 76 | 77 | true 78 | 79 | false 80 | 81 | (def my-data-structure 82 | [{:name "" :age "" :address ""} 83 | {:name "" :age "" :address ""} 84 | ,,,]) 85 | 86 | 87 | 88 | 89 | 90 | ;; Maps 91 | ;; - dictionaries 92 | 93 | 94 | ;; Imumability 95 | (conj [1 2 3] 4) 96 | ;; => [1 2 3 4] 97 | 98 | (def my-data [1 2 3]) 99 | 100 | (conj [1 2 3] 4) 101 | ;; => [1 2 3 4] 102 | 103 | (conj my-data 7) 104 | ;; => [1 2 3 7] 105 | 106 | 107 | 108 | ;; sets 109 | 110 | (hash-set 1 1 2 2) 111 | ;; => #{1 2} 112 | 113 | #{1 2} 114 | -------------------------------------------------------------------------------- /src/practicalli/getting_started.clj: -------------------------------------------------------------------------------- 1 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 2 | ;; 3 | ;; Brave Clojure Redux 4 | ;; - review and elaboration of the examples from the book 5 | ;; 6 | ;; Chapter 3: Getting started 7 | ;; - syntax (forms, expressions) 8 | ;; - 9 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 10 | 11 | ;; Practicalli Clojure provides a quick guide to Clojure 12 | ;; which contains an embedded REPL in the webpage so you can try out the code and see what happens when its changed 13 | ;; https://practicalli.github.io/clojure/first-taste-of-clojure.html 14 | 15 | 16 | (ns practicalli.getting-started 17 | (:gen-class)) 18 | 19 | 20 | ;; Forms / Expressions 21 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 22 | 23 | ;; A form is any valid piece of Clojure code, 24 | ;; if code does not evaluate then it is not of a correct form. 25 | ;; Clojure forms are also referred to as an expression, especially when combined 26 | 27 | ;; Clojure forms predominantly exist in some data structure, 28 | ;; most commonly in a list as an expression or part of an expression 29 | 30 | ;; Clojure forms within a list express a call to a function, usually with arguments 31 | 32 | (* 1 2 3 4) 33 | (inc 9) 34 | (even? 42) 35 | (str "Hello" " " "World") 36 | (map inc [1 3 5 7 9]) 37 | (range 5 10) 38 | (apply * (range 5 10)) 39 | (apply + (concat '(1 2 3 4) (range 5 10))) 40 | 41 | ;; The form at the start of a () list is treated as the name of a function to call, 42 | ;; any further forms in the list being arguments to that function call. 43 | ;; Many clojure.core functions take variable number of arguments 44 | 45 | ;; Clojure uses prefix notation and () lists to identify precisely how code should be evaluated. 46 | (+ 4 2 (* 6 3 2)) 47 | 48 | ;; nested lists are evaluated first and replaced by their return result 49 | (+ 4 2 36) 50 | 51 | ;; then the whole expression is evaluated and a value is returned 52 | 53 | ;; Try evaluating the nested expressions in this example 54 | (+ 3 (* 2 (- 7 2) 4) (/ 16 4)) 55 | 56 | 57 | ;; Literal values 58 | ;; Literal representation of a form, e.g 42 "I am a string" :keywords 59 | ;; can be evaluated all by themselves, although not all that useful 60 | 61 | ;; For example, the simplest Hello World code in Clojure is a literal string 62 | 63 | "Hello World" 64 | 65 | ;; Evaluating this form will return the same value, as the form is the value it represents 66 | 67 | 68 | ;; NOTE: A form always returns a value when evaluated 69 | 70 | 71 | 72 | ;; Control flow 73 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 74 | 75 | 76 | 77 | (if (> 1 5 ) 78 | "true" 79 | "false") 80 | 81 | (if (> 5 1) 82 | "true" 83 | "false") 84 | 85 | (when (> 5 1) 86 | "true") 87 | 88 | ;; Idioms 89 | ;; https://guide.clojure.style/#idioms 90 | 91 | true 92 | false 93 | 94 | (false? 1) 95 | 96 | (if [1 2 3] 97 | "true" 98 | "false") 99 | 100 | (if false 101 | "true") 102 | 103 | (not (empty? [1 2 3])) 104 | 105 | (seq [1 2 3]) 106 | (seq []) 107 | 108 | (if (seq []) 109 | "true" 110 | "false");; => "false" 111 | 112 | (if (seq [1 2 3]) 113 | "true" 114 | "false");; => "true" 115 | 116 | (or PORT 8080) 117 | 118 | (and ,,, ) 119 | 120 | 121 | 122 | 123 | (if 1 124 | "true") 125 | 126 | (if [] 127 | "true") 128 | 129 | (if (seq []) 130 | "true") 131 | 132 | 133 | ;; other languages 134 | x = 3 135 | 136 | 137 | (= 1 1 1 1 1 1 1 (- 2 1) (* 1 1)) 138 | 139 | (if (= 2 1 1 1 1 1 1 1 (- 2 1) (* 1 1)) 140 | "true" 141 | (str "false" " or " "falsey")) 142 | 143 | (= true false) 144 | ;; => false 145 | (= true nil) 146 | ;; => false 147 | 148 | 149 | (= true (= 1 1)) 150 | 151 | (= [1 2 3] [1 2 3]) 152 | (= [1 2 3] '(1 2 3)) 153 | ;; => true 154 | 155 | (quote (1 2 3)) 156 | 157 | 158 | ;; Functions 159 | 160 | 161 | (def x-player "X") 162 | (def y-player "y") 163 | 164 | 165 | 166 | (defn player-turn [player] 167 | ,,,) 168 | 169 | (player-turn y-player) 170 | 171 | (defn winner? [game-state players] 172 | (str "winner is " (first players))) 173 | 174 | (winner? {:scores []} [x-player y-player]) 175 | 176 | ;; (def severity :mild) 177 | ;; (def error-message "OH GOD! IT'S A DISASTER! WE'RE ") 178 | ;; (if (= severity :mild) 179 | ;; (def error-message (str error-message "MILDLY INCONVENIENCED!")) 180 | ;; (def error-message (str error-message "DOOOOOOOMED!"))) 181 | 182 | 183 | (let [name value] 184 | (str name)) 185 | 186 | (let [company-name "Practicalli"] 187 | (str company-name) 188 | ,,, 189 | ,,, 190 | ) 191 | 192 | (defn my-algorithm 193 | [blah] 194 | (let [company-name "jr0cket" 195 | company-slogan (str company-name " is awesome and waffley")] 196 | company-slogan 197 | ,,, 198 | ,,, 199 | )) 200 | 201 | (my-algorithm "") 202 | 203 | 204 | (defn -main 205 | "I don't do a whole lot ... yet." 206 | [& args] 207 | (println "Hello, World!")) 208 | 209 | 210 | (+ 1 2 3 4 ) 211 | 212 | (apply + (range 5 10)) 213 | 214 | (apply + (concat '(1 2 3 4) (range 5 10))) 215 | -------------------------------------------------------------------------------- /src/practicalli/destructuring_tictactoe.clj: -------------------------------------------------------------------------------- 1 | (ns practicalli.destructuring-tictactoe) 2 | 3 | 4 | 5 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 6 | ;; problem 73 - analyse tic-tac-toe board 7 | 8 | ;; A tic-tac-toe board is represented by a two dimensional vector. X is represented by :x, O is represented by :o, and empty is represented by :e. 9 | ;; A player wins by placing three Xs or three Os in a horizontal, vertical, or diagonal row. 10 | ;; Write a function which analyzes a tic-tac-toe board and returns :x if X has won, :o if O has won, and nil if neither player has won. 11 | 12 | 13 | ;; Define some tic-tac-toe board pattern to test 14 | 15 | (def board-empty [[:e :e :e] 16 | [:e :e :e] 17 | [:e :e :e]]) 18 | 19 | (def board-x-first-row [[:x :x :x] 20 | [:e :e :e] 21 | [:e :e :e]]) 22 | 23 | (def board-o-diagonal [[:o :e :e] 24 | [:e :o :e] 25 | [:e :e :o]]) 26 | 27 | ;; Algorithm 28 | ;; 1) Extract all the values from the board (destructuring with let) 29 | ;; 2) Pattern match the values to winning combinations (=) 30 | ;; 3) If any combination matched with :x or :o then return winner (or) 31 | 32 | 33 | ;; Destructuring will bind a local name to a value from the board pattern. 34 | ;; In this case, a b and c will each represent a line of the board 35 | 36 | (let [[a b c] board-empty] 37 | [a b c]) 38 | ;; => [[:e :e :e] [:e :e :e] [:e :e :e]] 39 | 40 | ;; Now a b and c will represent the elements of the first row of the board pattern. 41 | 42 | (let [[[a b c]] board-x-first-row] 43 | [a b c]) 44 | ;; => [:o :e :e] 45 | 46 | 47 | ;; So we can pull out the whole board using the following let expression 48 | 49 | (let [[[a b c] 50 | [d e f] 51 | [g h i]] board-o-diagonal] 52 | [[a b c] [d e f] [g h i]]) 53 | ;; => [[:o :e :e] [:e :o :e] [:e :e :o]] 54 | 55 | 56 | ;; let can be used with functions as well as values 57 | ;; we can call the function using the local name winner, 58 | ;; passing a board and a player character to see if they have won. 59 | ;; We just use a single winning pattern here for simplicity. 60 | (let [winner (fn [[[a b c] 61 | [d e f] 62 | [g h i]] 63 | player] 64 | (= player a b c))] 65 | (winner board-x-first-row :x)) 66 | ;; => true 67 | 68 | 69 | ;; We can streamline the let binding and anonymous function definition a little 70 | ;; letfn creates a function within the scope of the let expression. 71 | ;; letfn takes the name of the function to create followed by its arguments, 72 | ;; in this case the board patter we are destructuring and the player character. 73 | ;; For example: 74 | 75 | (letfn [(winner [[[a b c] 76 | [d e f] 77 | [g h i]] 78 | player])]) 79 | 80 | ;; Using macro-expansion we can see the letfn creates a function called winner 81 | ;; with two arguments: a board pattern and player 82 | (letfn* [winner (fn winner [[[a b c] [d e f] [g h i]] player])]) 83 | 84 | ;; We can call the local winner function whilst inside the letfn expression, 85 | ;; however we have not yet added any behaviour to that function. 86 | 87 | 88 | ;; Adding a check to see if a player has one the first row of the board as a first 89 | ;; attempt at defining the function behaviour. 90 | ;; If the first row matches the player character then we should get true. 91 | 92 | (letfn [(winner [[[a b c] 93 | [d e f] 94 | [g h i]] 95 | player] 96 | (println (str "first row: " [a b c])) ;; debugging 97 | (= player a b c))] 98 | (winner board-x-first-row :x)) 99 | ;; => true 100 | 101 | ;; Note: A simple println statement was added to show the intermediate values, 102 | ;; as an example of a simple debugging statement 103 | 104 | ;; Now we have the basic logic, we can add the patterns for all the winning combinations 105 | ;; We only need one pattern to match, so we wrap the patterns in an or function 106 | ;; To test both players, we add a condition around calling the winner function for each player 107 | 108 | 109 | (letfn [(winner [[[a b c] 110 | [d e f] 111 | [g h i]] 112 | player] 113 | (or (= player a b c) 114 | (= player d e f) 115 | (= player g h i) 116 | (= player a d g) 117 | (= player b e h) 118 | (= player c f i) 119 | (= player a e i) 120 | (= player c e g)))] 121 | (cond (winner board-x-first-row :x) :x 122 | (winner board-x-first-row :o) :o 123 | :else (str "No Winner for this board: " board-x-first-row))) 124 | ;; => :x 125 | 126 | ;; The letfn expression was tested with each of the three board patterns defined, giving the correct results. 127 | 128 | ;; 4Clojure entered solution 129 | 130 | (fn [board] 131 | (letfn [(winner [[[a b c] 132 | [d e f] 133 | [g h i]] 134 | player] 135 | (or (= player a b c) 136 | (= player d e f) 137 | (= player g h i) 138 | (= player a d g) 139 | (= player b e h) 140 | (= player c f i) 141 | (= player a e i) 142 | (= player c e g)))] 143 | (cond (winner board :x) :x 144 | (winner board :o) :o 145 | :else nil))) 146 | 147 | 148 | ;; Alternative solutions 149 | 150 | ;; I quite like the solution by tclamb, its similar to the submitted solution in approach 151 | 152 | (fn winner [[[a b c :as top] 153 | [d e f :as middle] 154 | [g h i :as bottom]]] 155 | (let [left [a d g] 156 | center [b e h] 157 | right [c f i] 158 | forward [a e i] 159 | backward [c e g]] 160 | (when-let [winner (some #(when (or (every? #{:x} %) (every? #{:o} %)) %) 161 | [top middle bottom 162 | left center right 163 | forward backward])] 164 | (first winner)))) 165 | -------------------------------------------------------------------------------- /src/practicalli/functions.clj: -------------------------------------------------------------------------------- 1 | (ns practicalli.functions) 2 | 3 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 4 | ;; Clojure functions 5 | ;; 6 | ;; Brave Clojure Chapter 3 - Do things - Functions 7 | ;; - calling functions 8 | ;; - macros and special forms 9 | ;; - defining functions 10 | ;; - anonymous functions 11 | ;; - returning functions (part of higher order functions) 12 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 13 | 14 | 15 | 16 | ;; Calling functions 17 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 18 | 19 | ;; (function-name) 20 | ;; (function-name argument) 21 | ;; (function-name argument1 argument2 argument3 ,,,) 22 | 23 | ;; Examples 24 | (+ 1 3 5 8 13 21) 25 | ;; => 51 26 | 27 | ;; nested functions 28 | ;; arguments evaluated before passing to the function 29 | (+ 1 (+ 1 2) (/ 25 5) 48/6 (- 24 11) (* 3 7)) 30 | 31 | 32 | ;; 'Interesting' Brave Clojure example 33 | ((or + -) 1 2 3) 34 | 35 | ;; deconstruct the above 36 | (or + -) 37 | ;; => #function[clojure.core/+] 38 | 39 | ;; Check for a Boolean true value, true or false 40 | (true? +) 41 | (true? true) 42 | (true? false) 43 | 44 | ;; Check for a truthy value (using conditionals from video 96) 45 | (if + 46 | "truthy" 47 | "falsey") 48 | 49 | (if nil 50 | "truthy" 51 | "falsey") 52 | 53 | ;; More interesting examples 54 | ;; and returns the last truthy value 55 | ((and (= 1 1) +) 1 2 3) 56 | 57 | 58 | ((first [+ 0]) 1 2 3) 59 | 60 | ;; Above examples demonstrate that functions return a value 61 | ;; A value will be interpreted as a function call if its the first element in a list 62 | 63 | ;; invalid function call examples 64 | ;; (1 2 3 4) 65 | ;; ("test" 1 2 3) 66 | 67 | 68 | ;; functions can take any expressions as arguments—including other functions 69 | 70 | (map inc [0 1 2 3]) 71 | ;; => (1 2 3 4) 72 | 73 | (= [1 2 3] '(1 2 3)) 74 | 75 | ;; Use map to iterate over a collection of values, transforming those values 76 | 77 | (def portfolio [{ :ticker "CRM" :lastTrade 233.12 :open 230.66} 78 | { :ticker "AAPL" :lastTrade 203.25 :open 204.50} 79 | { :ticker "MSFT" :lastTrade 29.12 :open 29.08 } 80 | { :ticker "ORCL" :lastTrade 21.90 :open 21.83 }]) 81 | 82 | (get { :ticker "CRM" :lastTrade 233.12 :open 230.66} :ticker) 83 | 84 | ({ :ticker "CRM" :lastTrade 233.12 :open 230.66} :ticker) 85 | (:ticker { :ticker "CRM" :lastTrade 233.12 :open 230.66} ) 86 | 87 | 88 | (map :ticker portfolio) 89 | 90 | (map (fn [stock-price] (assoc stock-price :open (:lastTrade stock-price))) 91 | portfolio) 92 | 93 | (map #(assoc % :open (:lastTrade %)) portfolio) 94 | 95 | 96 | ;; The composability of Clojure enables abstractions to be created base 97 | 98 | 99 | 100 | ;; Special forms 101 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 102 | 103 | ;; Building blocks of the language 104 | ;; https://clojure.org/reference/special_forms 105 | 106 | ;; def, if, 107 | 108 | ;; Cannot be used as arguments to other functions 109 | ;; have different evaluation rules 110 | ;; e.g. if 111 | 112 | (if true 113 | "true" 114 | "false") 115 | 116 | 117 | 118 | ;; defining functions 119 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 120 | 121 | ;; Different forms of function definition 122 | 123 | ;; defn 124 | 125 | ;; def fn 126 | ;; - defn is a macro 127 | 128 | 129 | ;; anonymous function 130 | ;; this way of defining a function does not have an external name that can be referenced 131 | ;; it can have a local name, so a function can call itself by name or via recur 132 | 133 | (fn update-stock [stock-price] (assoc stock-price :open (:lastTrade stock-price))) 134 | 135 | 136 | (fn update-stock [stock-price] (update-stock {})) 137 | 138 | ;; (update-stock) 139 | 140 | ;; Brave Clojure examples 141 | 142 | (defn too-enthusiastic 143 | "Return a cheer that might be a bit too enthusiastic" 144 | [name] 145 | (str "OH. MY. GOD! " name " YOU ARE MOST DEFINITELY LIKE THE BEST " 146 | "MAN SLASH WOMAN EVER I LOVE YOU AND WE SHOULD RUN AWAY SOMEWHERE")) 147 | 148 | (too-enthusiastic "practialli") 149 | 150 | 151 | 152 | 153 | ;; Parameters and arity 154 | ;; You can have multi-variadic functions too 155 | ;; one function that behaves differently dependant on the number or arugments passed 156 | 157 | (defn no-params 158 | [] 159 | "I take no parameters!") 160 | (defn one-param 161 | [x] 162 | (str "I take one parameter: " x)) 163 | (defn two-params 164 | [x y] 165 | (str "Two parameters! That's nothing! Pah! I will smoosh them " 166 | "together to spite you! " x y)) 167 | 168 | 169 | (defn x-chop 170 | "Describe the kind of chop you're inflicting on someone" 171 | ([name] 172 | (x-chop name "karate")) 173 | ([name chop-type] 174 | (str "I " chop-type " chop " name "! Take that!"))) 175 | 176 | (x-chop "Kanye West" "slap") 177 | (x-chop "Kanye East") 178 | 179 | 180 | (defn codger-communication 181 | [whippersnapper] 182 | (str "Get off my lawn, " whippersnapper "!!!")) 183 | 184 | (defn codger 185 | [& whippersnappers] 186 | (map codger-communication whippersnappers)) 187 | 188 | (codger "Billy" "Anne-Marie" "The Incredible Bulk") 189 | 190 | 191 | ;; Practicalli examples 192 | 193 | (defn hello-polly-function 194 | ([] "Hello Hackference") 195 | ([name] (str "Hello " name ", hope your talk is awesome at Hackference"))) 196 | 197 | (hello-polly-function) 198 | (hello-polly-function "Cristiano") 199 | 200 | 201 | ;; Functions can pack extra arguments up in a sequence for you, its the & notation that is important, although its a fairly common convention to use args (better if its given a more meaningful name though) 202 | 203 | (defn count-args [& args] 204 | (str "You passed " (count args) " args: " args)) 205 | (count-args 1 2 3) 206 | 207 | ; You can mix regular and packed arguments 208 | (defn hello-count [name & args] 209 | (str "Hello " name ", you passed " (count args) " extra args")) 210 | (hello-count "Finn" 1 2 3) 211 | (hello-count) 212 | 213 | (count-args) 214 | 215 | 216 | ;; Destructuring 217 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 218 | ;; https://clojure.org/guides/destructuring 219 | 220 | ;; Positional - e.g. for vectors 221 | ;; Associative - e.g. for hash-maps 222 | 223 | 224 | 225 | 226 | ;; Macros 227 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 228 | 229 | ;; Calling macros is the same as calling functions, 230 | ;; although macros lack composability 231 | 232 | ;; except that a macro generates new code and then evaluates that 233 | -------------------------------------------------------------------------------- /src/practicalli/exercise_shires_next_top_model.clj: -------------------------------------------------------------------------------- 1 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 2 | ;; Shire;s Next Top Model 3 | ;; 4 | ;; Practicalli does not condone aggression or violence 5 | ;; towards Hobbits or any living creatures 6 | ;; either mythical or perceived as real 7 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 8 | 9 | 10 | (ns practicalli.exercise-shires-next-top-model) 11 | 12 | 13 | ;;;; Modeling the Hobbits 14 | 15 | ;; Each body part of a hobbit is defined with a name and relative size 16 | ;; indicating how likely it is to be hit 17 | ;; e.g. chest is size 10 as it is a bigger target than ear at size 1 18 | 19 | ;; For efficiency, where a hobbit has a pair of body parts, only one of a pair is defined, 20 | ;; so only left ear, left eye, etc. 21 | 22 | ;; A function will be created that takes the single item of a pair 23 | ;; and makes a symmetric hobbit with 2 ears, 2 eyes, etc. 24 | 25 | 26 | (def asym-hobbit-body-parts 27 | [{:name "head" :size 3} 28 | {:name "left-eye" :size 1} 29 | {:name "left-ear" :size 1} 30 | {:name "mouth" :size 1} 31 | {:name "nose" :size 1} 32 | {:name "neck" :size 2} 33 | {:name "left-shoulder" :size 3} 34 | {:name "left-upper-arm" :size 3} 35 | {:name "chest" :size 10} 36 | {:name "back" :size 10} 37 | {:name "left-forearm" :size 3} 38 | {:name "abdomen" :size 6} 39 | {:name "left-kidney" :size 1} 40 | {:name "left-hand" :size 2} 41 | {:name "left-knee" :size 2} 42 | {:name "left-thigh" :size 4} 43 | {:name "left-lower-leg" :size 3} 44 | {:name "left-achilles" :size 1} 45 | {:name "left-foot" :size 2}]) 46 | 47 | ;; Body parts is a single collection, specifically a vector 48 | ;; each element of the collection is a hash-map 49 | ;; each hash-map contains key-value pairs 50 | ;; keys in the hash-map are defined using Clojure keywords 51 | 52 | (get {:name "left-foot" :size 2} :name) 53 | (:name {:name "left-foot" :size 2}) 54 | ({:name "left-foot" :size 2} :name) 55 | 56 | 57 | ;;;; Background: working with sequences - first and rest 58 | 59 | (first asym-hobbit-body-parts) 60 | (rest asym-hobbit-body-parts) 61 | 62 | 63 | (let [name "value"]) 64 | 65 | ;; Using destructuring 66 | (let [[first-body-part & rest-of-parts] asym-hobbit-body-parts] 67 | rest-of-parts) 68 | 69 | 70 | 71 | ;;;; Making a whole hobbit 72 | 73 | ;; A function to generate a right body part, given a left body part 74 | 75 | (defn matching-part 76 | [part] 77 | {:name (clojure.string/replace (:name part) #"^left-" "right-") 78 | :size (:size part)}) 79 | 80 | 81 | ;; Aside: regular expressions 82 | ;; regular expressions are common in all programming languages 83 | ;; they are very simple to use in Clojure 84 | 85 | ;; #"^left-" is a regular expression pattern that matches any string starting with "left-" 86 | ;; The carat, ^, specifies the pattern should match from the beginning of the string 87 | ;; re-find uses a pattern to check a string, returning the matched string or nil if there is no match 88 | 89 | (re-find #"^left-" "left-eye") 90 | 91 | (re-find #"^left-" "cleft-chin") 92 | 93 | (re-find #"^left-" "wongleblart") 94 | 95 | 96 | ;; Testing the matching part function 97 | 98 | (matching-part {:name "left-eye" :size 1}) 99 | 100 | (matching-part {:name "head" :size 3}) 101 | 102 | 103 | ;; A function to iterate over the hobbit body parts data structure 104 | 105 | (defn symmetrize-body-parts 106 | "Expects a seq of maps that have a :name and :size" 107 | [asym-body-parts] 108 | (loop [remaining-asym-parts asym-body-parts 109 | final-body-parts []] 110 | (if (empty? remaining-asym-parts) 111 | final-body-parts 112 | (let [[part & remaining] remaining-asym-parts] 113 | (recur remaining 114 | (into final-body-parts 115 | (set [part (matching-part part)]))))))) 116 | 117 | 118 | (symmetrize-body-parts asym-hobbit-body-parts) 119 | ;; => [{:name "head", :size 3} {:name "left-eye", :size 1} {:name "right-eye", :size 1} {:name "left-ear", :size 1} {:name "right-ear", :size 1} {:name "mouth", :size 1} {:name "nose", :size 1} {:name "neck", :size 2} {:name "left-shoulder", :size 3} {:name "right-shoulder", :size 3} {:name "right-upper-arm", :size 3} {:name "left-upper-arm", :size 3} {:name "chest", :size 10} {:name "back", :size 10} {:name "left-forearm", :size 3} {:name "right-forearm", :size 3} {:name "abdomen", :size 6} {:name "left-kidney", :size 1} {:name "right-kidney", :size 1} {:name "left-hand", :size 2} {:name "right-hand", :size 2} {:name "right-knee", :size 2} {:name "left-knee", :size 2} {:name "right-thigh", :size 4} {:name "left-thigh", :size 4} {:name "right-lower-leg", :size 3} {:name "left-lower-leg", :size 3} {:name "right-achilles", :size 1} {:name "left-achilles", :size 1} {:name "right-foot", :size 2} {:name "left-foot", :size 2}] 120 | 121 | 122 | ;; Explanations 123 | ;; recur calls loop with updated values for its arguments 124 | ;; recur is efficient as it re-uses memory (software tail recursion) 125 | ;; loop binds values to names, just like let 126 | ;; let binds local names to values using destructuring, part is used several times in the recur expression 127 | ;; into conjoins a value onto the given data structure, initially a vector, then vector of parts 128 | ;; set is used to avoid duplicates when a part has no right equivalent 129 | ;; part is the original unchanged body part 130 | 131 | 132 | ;; Using set to avoid duplicates and into to put into a vector 133 | 134 | (into [] (set [{:name "head" :size 3} {:name "head" :size 3}])) 135 | 136 | (into [] (set [{:name "left-ear" :size 1} 137 | {:name "right-ear" :size 1}])) 138 | 139 | 140 | 141 | ;; Rich comment block with redefined vars ignored 142 | #_{:clj-kondo/ignore [:redefined-var]} 143 | (comment 144 | 145 | ;; Alternative implementation without the let 146 | 147 | (defn symmetrize-body-parts 148 | "Expects a seq of maps that have a :name and :size" 149 | [asym-body-parts] 150 | (loop [remaining-asym-parts asym-body-parts 151 | final-body-parts []] 152 | (if (empty? remaining-asym-parts) 153 | final-body-parts 154 | (recur (rest remaining-asym-parts) 155 | (into final-body-parts 156 | (set [(first remaining-asym-parts) (matching-part (first remaining-asym-parts) )])))))) 157 | 158 | 159 | (symmetrize-body-parts asym-hobbit-body-parts) 160 | 161 | ) ;; End of rich comment block 162 | 163 | 164 | 165 | ;; Going loopy 166 | 167 | (loop [iteration 0] 168 | (println (str "Iteration " iteration)) 169 | (if (> iteration 3) 170 | (println "Goodbye!") 171 | (recur (inc iteration)))) 172 | 173 | ;; Alternative to loop recur - a recursive function 174 | 175 | (defn recursive-printer 176 | ([] 177 | (recursive-printer 0)) 178 | ([iteration] 179 | (println "Iteration" iteration) ;; fixed small bug 180 | (if (> iteration 3) 181 | (println "Goodbye!") 182 | (recursive-printer (inc iteration))))) 183 | 184 | (recursive-printer) 185 | (recursive-printer 2) 186 | 187 | 188 | ;; Rich comment block with redefined vars ignored 189 | #_{:clj-kondo/ignore [:redefined-var]} 190 | (comment 191 | 192 | ;; Using `recur` with the recursive function make it more memory efficient 193 | 194 | (defn recursive-printer 195 | ([] 196 | (recursive-printer 0)) 197 | ([iteration] 198 | (println "Iteration" iteration) ;; fixed small bug 199 | (if (> iteration 3) 200 | (println "Goodbye!") 201 | (recur (inc iteration))))) 202 | 203 | (recursive-printer) 204 | 205 | ) ;; End of rich comment block 206 | 207 | 208 | ;; Conceptual abstraction levels 209 | ;; transducers 210 | ;; reduce & reducing functions (map, apply, lots of other clojure.core functions) 211 | ;; recursive function 212 | ;; loop recur 213 | 214 | 215 | 216 | ;;;; reduce 217 | 218 | ;; reduce takes a function and a collection of values 219 | 220 | (reduce + [1 2 3 4]) 221 | 222 | ;; This is the same as 223 | 224 | (+ (+ (+ 1 2) 3) 4) 225 | 226 | 227 | ;; The reduce function works according to the following steps: 228 | 229 | ;; Apply function to first two elements, i.e. (+ 1 2) 230 | ;; Apply function to result and next element. Result of step 1 is 3, the next element is 3. So we get (+ 3 3). 231 | ;; Repeat step 2 for every remaining element in the sequence. 232 | 233 | 234 | ;; Show: eval and replace 235 | 236 | (+ (+ (+ 1 2) 3) 4) 237 | 238 | 239 | ;; Providing an initial value to reduce 240 | ;; Apply funciton to initial value and first element of the sequence 241 | 242 | (reduce + 15 [1 2 3 4]) 243 | 244 | (+ (+ (+ (+ 15 1) 2) 3) 4) 245 | 246 | 247 | 248 | 249 | ;;;; Refactor the body parts generator 250 | 251 | ;; Use reduce with an anonymous function, 252 | ;; an initial value of an empty vector 253 | ;; and the asymetric body parts collection 254 | 255 | (defn better-symmetrize-body-parts 256 | "Expects a seq of maps that have a :name and :size" 257 | [asym-body-parts] 258 | (reduce (fn [final-body-parts part] 259 | (into final-body-parts (set [part (matching-part part)]))) 260 | [] 261 | asym-body-parts)) 262 | 263 | 264 | (better-symmetrize-body-parts asym-hobbit-body-parts) 265 | 266 | 267 | ;; Rich comment block with redefined vars ignored 268 | #_{:clj-kondo/ignore [:redefined-var]} 269 | (comment 270 | 271 | ;; If the reducing function is large, it could be defined as a separate named function 272 | ;; Also useful if the reducing function should be used in other places in the code 273 | 274 | (defn generate-body-part [final-body-parts part] 275 | (into final-body-parts 276 | (set [part (matching-part part)]))) 277 | 278 | (defn better-symmetrize-body-parts 279 | "Expects a seq of maps that have a :name and :size" 280 | [asym-body-parts] 281 | (reduce generate-body-part 282 | [] 283 | asym-body-parts)) 284 | 285 | (better-symmetrize-body-parts asym-hobbit-body-parts) 286 | 287 | 288 | ) ;; End of rich comment block 289 | 290 | 291 | 292 | 293 | ;;;; Determine if a body part is hit 294 | 295 | (defn hit 296 | [asym-body-parts] 297 | (let [sym-parts (better-symmetrize-body-parts asym-body-parts) 298 | body-part-size-sum (reduce + (map :size sym-parts)) 299 | target (rand body-part-size-sum)] 300 | (loop [[part & remaining] sym-parts 301 | accumulated-size (:size part)] 302 | (if (> accumulated-size target) 303 | part 304 | (recur remaining (+ accumulated-size (:size (first remaining)))))))) 305 | 306 | 307 | 308 | (hit asym-hobbit-body-parts) 309 | 310 | 311 | 312 | ;; What if there were more than two body parts 313 | 314 | 315 | 316 | 317 | 318 | 319 | ;;;; reduce vs apply 320 | 321 | ;; reduce 322 | (+ (+ (+ 1 2) 3) 4) 323 | 324 | ;; apply 325 | (apply + [1 2 3 4]) 326 | 327 | (+ 1 2 3 4) 328 | --------------------------------------------------------------------------------