This commit is contained in:
Josh Abrams 2016-02-29 23:26:11 +00:00
commit aac27b2d29
53 changed files with 601 additions and 1529 deletions

32
01_equalities.clj Normal file
View file

@ -0,0 +1,32 @@
; "We shall contemplate truth by testing reality, via equality"
(= true true)
; "To understand reality, we must compare our expectations against reality"
(= 2 (+ 1 1))
; "You can test equality of many things"
(= 5 (+ 3 4) 7 (+ 2 __))
; "Some things may appear different, but be the same"
(= true (= 2 2/1))
; "You cannot generally float to heavens of integers"
(= false (= 2 2.0))
; "But a looser equality is also possible"
(= true (== 2.0 2))
; "Something is not equal to nothing"
(= true (not (= 1 nil)))
; "Strings, and keywords, and symbols: oh my!"
(= false (= "hello" :hello 'hello))
; "Make a keyword with your keyboard"
(= :hello (keyword "hello"))
; "Symbolism is all around us"
(= 'hello (symbol "hello"))
; "When things cannot be equal, they must be different"
(not= :fill-in-the-blank "anything here")

68
02_strings.clj Normal file
View file

@ -0,0 +1,68 @@
; "A string is nothing more than text surrounded by double quotes"
(= "hello" "hello")
; "But double quotes are just magic on top of something deeper"
(= "world" (str 'world))
; "You can do more than create strings, you can put them together"
(= "Cool right?" (str "Cool" " right?")) ;; J - Whitespace!!
; "You can even get certain characters"
(= \C (get "Characters" 0)) ;; J - get returns character or item number at a given index
; "Or even count the characters"
(= 11 (count "Hello World")) ;; J - count returns the length of a string or list
; "But strings and characters are not the same"
(= false (= \c "c"))
; "What if you only wanted to get part of a string?"
(= "World" (subs "Hello World" 6 11)) ;; J - subs returns a substring of a given string and can take
;; two arguments. The the first one is the begining index of the
;; substring and the second one is the last
; "How about joining together elements in a list?"
(= "123" (string/join '(1 2 3)))
; "What if you wanted to separate them out?"
(= "1, 2, 3" (clojure.string/join ", " '(1 2 3))) ;; J - join does non seperate with whitespace by default
; "Maybe you want to separate out all your lines"
(= ["1" "2" "3"] (clojure.string/split-lines "1\n2\n3"))
; "You may want to make sure your words are backwards"
(= "olleh" (string/reverse "hello"))
; "Maybe you want to find the index of the first occurence of a substring"
(= 0 (clojure.string/index-of "hello world" __))
; "Or maybe the last index of the same"
(= __ (clojure.string/last-index-of "hello world, hello" "hello"))
; "But when something doesn't exist, nothing is found"
(= (clojure.string/index-of "hello world" "bob")) ;; J - Errored out
; "Sometimes you don't want whitespace cluttering the front and back"
(= "hello world" (clojure.string/trim " \nhello world \t \n"))
; "You can check if something is a char"
(= true (char? \c))
; "But it may not be"
(= false (char? "a"))
; "But chars aren't strings"
(= false (string? \b))
; "Strings are strings"
(= true (string? __))
; "Some strings may be blank"
(= true (clojure.string/blank? ""))
; "Even if at first glance they aren't"
(= true (clojure.string/blank? " \n \t "))
; "However, most strings aren't blank"
(= false (clojure.string/blank? "hello?\nare you out there?"))

48
03_lists.clj Normal file
View file

@ -0,0 +1,48 @@
; "Lists can be expressed by function or a quoted form"
(= '(1 2 3 4 5) (list 1 2 3 4 5))
; "They are Clojure seqs (sequences), so they allow access to the first"
(= 1 (first '(1 2 3 4 5)))
; "As well as the rest"
(= 2 3 4 5 (rest '(1 2 3 4 5))) ;; J -
; "Count your blessings"
(= 3 (count '(dracula dooku chocula)))
; "Before they are gone"
(= 0 (count '()))
; "The rest, when nothing is left, is empty"
(= (rest '(100))) ;; J - returns nothing.
; "Construction by adding an element to the front is easy"
(= :a :b :c :d :e (cons :a '(:b :c :d :e))) ;; J - cons add's the element on the left to the beginning
;; of the list on the right and returns a seq.
;;(EX: (cons [1 2] '(3 4 5)) => '([1 2] 3 4 5)
; "Conjoining an element to a list isn't hard either"
(= :a :b :c :d :e (conj '(:a :b :c :d) :e))
;; J - conj works like cons, but takes any number of arguments from
;; the right and adds them to the beginning of the list on the left
;; returning them in the structure of whatever the list on the left is defined
;; as originally. (EX: (conj [4 5 6] 1 2 3) => [1 2 3 4 5 6]
; "You can use a list like a stack to get the first element"
(= :a (peek '(:a :b :c :d :e))) ;; J - returns the first element
; "Or the others"
(= (:b) (pop '(:a :b :c :d :e))) ;; J - pops the first element and returns the others as a seq
; "But watch out if you try to pop nothing"
(= (try ;; J - Catch raises an error similar to 'raise' in Ruby.
(pop '())
(catch IllegalStateException e
"No dice!")))
; "The rest of nothing isn't so strict"
(= () (try
(rest '())
(catch IllegalStateException e
"No dice!")))

30
04_vectors.clj Normal file
View file

@ -0,0 +1,30 @@
; "You can use vectors in clojure as array-like structures"
(= 1 (count [42]))
; "You can create a vector from a list"
(= [1] (vec '(1)))
; "Or from some elements"
(= [nil nil] (vector nil nil))
; "But you can populate it with any number of elements at once"
(= [1 2] (vec '(1 2)))
; "Conjoining to a vector is different than to a list"
(= __ (conj [111 222] 333))
; "You can get the first element of a vector like so"
(= :peanut (first [:peanut :butter :and :jelly]))
; "And the last in a similar fashion"
(= :jelly (last [:peanut :butter :and :jelly]))
; "Or any index if you wish"
(= :jelly (nth [:peanut :butter :and :jelly] 3)) ;; J - index #'s still start with 0 in Clojure.
; "You can also slice a vector"
(= [:butter :and] (subvec [:peanut :butter :and :jelly] 1 3)) ;; J - slicing with subvec includes first index arg and excludes second
; "Equality with collections is in terms of values"
(= true (list 1 2 3) (vector 1 2 3)) ;; J - lists and vectors are comparable.

18
05_sets.clj Normal file
View file

@ -0,0 +1,18 @@
; "You can create a set by converting another collection"
(= #{3} (set [3]))
; "Counting them is like counting other collections"
(= 3 (count #{1 2 3}))
; "Remember that a set is a *mathematical* set"
(= #{1 2 3 4 5} (set '(1 1 2 2 3 3 4 4 5 5))) ;; J - This creates a unique ORDERED set so it's always sorted.
; "You can ask clojure for the union of two sets"
(= #{1 2 3 4 5} (set/union #{1 2 3 4} #{2 3 5})) ;; J - Uniting sets still orders them and keeps elements unique
; "And also the intersection"
(= #{2 3} (set/intersection #{1 2 3 4} #{2 3 5})) ;; J - Creates a new set of common elements.
; "But don't forget about the difference"
(= #{1 4} (set/difference #{1 2 3 4 5} #{2 3 5}))

49
06_maps.clj Normal file
View file

@ -0,0 +1,49 @@
; "Don't get lost when creating a map"
(= {:a 1 :b 2} (hash-map :a 1 :b 2)) ;; J - Hash maps are key-value pairs defined as a linear
;; sequence. {key value key2 value2} etc.
; "A value must be supplied for each key"
(= {:a 1} (hash-map :a 1))
; "The size is the number of entries"
(= 2 (count {:a 1 :b 2}))
; "You can look up the value for a given key"
(= 2 (get {:a 1 :b 2} :b))
; "Maps can be used as functions to do lookups"
(= 1 ({:a 1 :b 2} :a)) ;; J - This is a bit confusing, but it essentially amounts to a keyword placed
;; on either side of a hash-map returning the key-word's matching value, the difference
;; being that the left object is called as a function on the right.
; "And so can keywords"
(= 1 (:a {:a 1 :b 2}))
; "But map keys need not be keywords"
(= "Sochi" ({2010 "Vancouver" 2014 "Sochi" 2018 "PyeongChang"} 2014))
; "You may not be able to find an entry for a key"
(= nil (get {:a 1 :b 2} :c)) ;; J - This caused a considerable delay in the REPL.
; "But you can provide your own default"
(= :key-not-found (get {:a 1 :b 2} :c :key-not-found)) ;; J - The third argument for get is the default.
; "You can find out if a key is present"
(= true (contains? {:a nil :b nil} :b))
; "Or if it is missing"
(= false (contains? {:a nil :b nil} :c))
; "Maps are immutable, but you can create a new and improved version"
(= {1 "January" 2 "February"} (assoc {1 "January"} 2 "February"))
; "You can also create a new version with an entry removed"
(= {1 "January"} (dissoc {1 "January" 2 "February"} 2)) ;; J - This can take any number of keys to dissociate.
; "Often you will need to get the keys, but the order is undependable"
(= (list 2010 2014 2018)
(sort (keys { 2014 "Sochi" 2018 "PyeongChang" 2010 "Vancouver"}))) ;; J - If you don't sort these, they are
;; essentially returned in random order.
; "You can get the values in a similar way"
(= (list "PyeongChang" "Vancouver" "PyeongChang")
(sort (vals {2010 "Vancouver" 2014 "Sochi" 2018 "PyeongChang"}))) ;; J - Sorts numerically or alphabetically

34
07_functions.clj Normal file
View file

@ -0,0 +1,34 @@
; "Calling a function is like giving it a hug with parentheses"
(= (square 9))
; "Functions are usually defined before they are used"
(= (multiply-by-ten 2))
; "But they can also be defined inline"
(= 10 ((fn [n] (* 5 n)) 2))
; "Or using an even shorter syntax"
(= 60 (#(* 15 %) 4)) ;; J - This is an important bit of sugar to memorize.
;; To make it easy, '#' initiates the declaration of an anonymous function
;; and % is the argument. So, in this case, # replaces 'fn [n]' and '%' replaces n
; "Even anonymous functions may take multiple arguments"
(= 15 (#(+ %1 %2 %3) 4 5 6))
; "Arguments can also be skipped"
(= 30 (#(* 15 %2) 1 2))
; "One function can beget another"
(= 9 ((fn [] ((fn [a b] (+ a b)) 4 5))))
; "Functions can also take other functions as input"
(= 20 ((fn [f] (f 4 5))
*)) ;; J - Mathematical operators in Clojure serve as functions.
; "Higher-order functions take function arguments"
(= 25 (#(% 5)
(fn [n] (* n n))))
; "But they are often better written using the names of functions"
(= 25 (defn square [n] (* n n)))

44
08_conditionals.clj Normal file
View file

@ -0,0 +1,44 @@
(defn explain-exercise-velocity [exercise-term]
(case exercise-term
:ten-mph :pretty-fast
:watching-tv :not-so-fast))
; "You will face many decisions"
(= :a (if (false? (= 4 5))
:a
:b))
; "Some of them leave you no alternative"
(= [] (if (> 4 3) ;; J - This is the equivalent of an if statement without an else.
[])) ;; returns nil if false as seen below.
; "And in such a situation you may have nothing"
(= nil (if (nil? 0)
[:a :b :c]))
; "In others your alternative may be interesting"
(= :glory (if (not (empty? ()))
:doom
:glory))
; "You may have a multitude of possible paths"
(let [x 5]
(= :your-road (cond (= x 3) :road-not-taken
(= x 2) :another-road-not-taken
:else :your-road))) ;; J - The syntax and formatting of this statement
;; are crucial in writing these statements correctly.
; "Or your fate may be sealed"
(= 'doom (if-not (zero? 1)
'doom
'more-doom))
; "In case of emergency, go fast"
(= :pretty-fast
(explain-exercise-velocity :ten-mph))
; "But admit it when you don't know what to do"
(= :not-so-fast
(explain-exercise-velocity :watching-tv))

View file

@ -0,0 +1,33 @@
; "The map function relates a sequence to another"
(= [4 8 12] (map (fn [x] (* 4 x)) [1 2 3]))
; "You may create that mapping"
(= [1 4 9 16 25] (map (fn [x] (* x x)) [1 2 3 4 5]))
; "Or use the names of existing functions"
(= '(false false true false false) (map nil? [:a :b nil :c :d]))
; "A filter can be strong"
(= () (filter (fn [x] false) '(:anything :goes :here)))
; "Or very weak"
(= '(:anything :goes :here) (filter (fn [x] true) '(:anything :goes :here)))
; "Or somewhere in between"
(= [10 20 30] (filter (fn [x] (<= x 30)) [10 20 30 40 50 60 70 80]))
; "Maps and filters may be combined"
(= [10 20 30] (map (fn [x] (* x 10))
(filter (fn [x] (<= x 3)) [1 2 3 4 5 6 7 8])))
; "Reducing can increase the result"
(= 24 (reduce (fn [a b] (* a b)) [1 2 3 4]))
; "You can start somewhere else"
(= 2400 (reduce (fn [a b] (* a b)) (cons 100 [1 2 3 4])))
; "Numbers are not the only things one can reduce"
(= "longest" (reduce (fn [a b]
(if (< (count a)) b a))
["which" "word" "is" "longest"]))

View file

@ -0,0 +1,26 @@
; "Some functions can be used in different ways - with no arguments"
(= __ (hello))
; "With one argument"
(= __ (hello "world"))
; "Or with many arguments"
(= __
(hello "Peter" "Paul" "Mary"))
; "Multimethods allow more complex dispatching"
(= "Bambi eats veggies."
(diet {:species "deer" :name "Bambi" :age 1 :eater :herbivore}))
; "Animals have different names"
(= "Thumper eats veggies."
(diet {:species "rabbit" :name "Thumper" :age 1 :eater :herbivore}))
; "Different methods are used depending on the dispatch function result"
(= "Simba eats animals."
(diet {:species "lion" :name "Simba" :age 1 :eater :carnivore}))
; "You may use a default method when no others match"
(= "I don't know what Rich Hickey eats."
(diet {:name "Rich Hickey"}))

25
11_lazy_sequences.clj Normal file
View file

@ -0,0 +1,25 @@
; "There are many ways to generate a sequence"
(= __ (range 1 5))
; "The range starts at the beginning by default"
(= __ (range 5))
; "Only take what you need when the sequence is large"
(= [0 1 2 3 4 5 6 7 8 9]
(take __ (range 100)))
; "Or limit results by dropping what you don't need"
(= [95 96 97 98 99]
(drop __ (range 100)))
; "Iteration provides an infinite lazy sequence"
(= __ (take 20 (iterate inc 0)))
; "Repetition is key"
(= [:a :a :a :a :a :a :a :a :a :a]
(repeat 10 __))
; "Iteration can be used for repetition"
(= (repeat 100 :hello)
(take 100 (iterate ___ :hello)))

View file

@ -1,36 +1,33 @@
(ns koans.12-sequence-comprehensions
(:require [koan-engine.core :refer :all]))
(meditations ; "Sequence comprehensions can bind each element in turn to a symbol"
"Sequence comprehensions can bind each element in turn to a symbol"
(= __ (= __
(for [x (range 6)] (for [x (range 6)]
x)) x))
"They can easily emulate mapping" ; "They can easily emulate mapping"
(= '(0 1 4 9 16 25) (= '(0 1 4 9 16 25)
(map (fn [x] (* x x)) (map (fn [x] (* x x))
(range 6)) (range 6))
(for [x (range 6)] (for [x (range 6)]
__)) __))
"And also filtering" ; "And also filtering"
(= '(1 3 5 7 9) (= '(1 3 5 7 9)
(filter odd? (range 10)) (filter odd? (range 10))
(for [x __ :when (odd? x)] (for [x __ :when (odd? x)]
x)) x))
"Combinations of these transformations is trivial" ; "Combinations of these transformations is trivial"
(= '(1 9 25 49 81) (= '(1 9 25 49 81)
(map (fn [x] (* x x)) (map (fn [x] (* x x))
(filter odd? (range 10))) (filter odd? (range 10)))
(for [x (range 10) :when __] (for [x (range 10) :when __]
__)) __))
"More complex transformations simply take multiple binding forms" ; "More complex transformations simply take multiple binding forms"
(= [[:top :left] [:top :middle] [:top :right] (= [[:top :left] [:top :middle] [:top :right]
[:middle :left] [:middle :middle] [:middle :right] [:middle :left] [:middle :middle] [:middle :right]
[:bottom :left] [:bottom :middle] [:bottom :right]] [:bottom :left] [:bottom :middle] [:bottom :right]]
(for [row [:top :middle :bottom] (for [row [:top :middle :bottom]
column [:left :middle :right]] column [:left :middle :right]]
__))) __))

View file

@ -1,35 +1,30 @@
(ns koans.13-creating-functions
(:require [koan-engine.core :refer :all]))
(defn square [x] (* x x)) ; "One may know what they seek by knowing what they do not seek"
(meditations
"One may know what they seek by knowing what they do not seek"
(= [__ __ __] (let [not-a-symbol? (complement symbol?)] (= [__ __ __] (let [not-a-symbol? (complement symbol?)]
(map not-a-symbol? [:a 'b "c"]))) (map not-a-symbol? [:a 'b "c"])))
"Praise and 'complement' may help you separate the wheat from the chaff" ; "Praise and 'complement' may help you separate the wheat from the chaff"
(= [:wheat "wheat" 'wheat] (= [:wheat "wheat" 'wheat]
(let [not-nil? ___] (let [not-nil? ___]
(filter not-nil? [nil :wheat nil "wheat" nil 'wheat nil]))) (filter not-nil? [nil :wheat nil "wheat" nil 'wheat nil])))
"Partial functions allow procrastination" ; "Partial functions allow procrastination"
(= 20 (let [multiply-by-5 (partial * 5)] (= 20 (let [multiply-by-5 (partial * 5)]
(___ __))) (___ __)))
"Don't forget: first things first" ; "Don't forget: first things first"
(= [__ __ __ __] (= [__ __ __ __]
(let [ab-adder (partial concat [:a :b])] (let [ab-adder (partial concat [:a :b])]
(ab-adder [__ __]))) (ab-adder [__ __])))
"Functions can join forces as one 'composed' function" ; "Functions can join forces as one 'composed' function"
(= 25 (let [inc-and-square (comp square inc)] (= 25 (let [inc-and-square (comp square inc)]
(inc-and-square __))) (inc-and-square __)))
"Have a go on a double dec-er" ; "Have a go on a double dec-er"
(= __ (let [double-dec (comp dec dec)] (= __ (let [double-dec (comp dec dec)]
(double-dec 10))) (double-dec 10)))
"Be careful about the order in which you mix your functions" ; "Be careful about the order in which you mix your functions"
(= 99 (let [square-and-dec ___] (= 99 (let [square-and-dec ___]
(square-and-dec 10)))) (square-and-dec 10)))

50
14_recursion.clj Normal file
View file

@ -0,0 +1,50 @@
(defn is-even? [n]
(if (= n 0)
__
(___ (is-even? (dec n)))))
(defn is-even-bigint? [n]
(loop [n n
acc true]
(if (= n 0)
__
(recur (dec n) (not acc)))))
(defn recursive-reverse [coll]
__)
(defn factorial [n]
__)
; "Recursion ends with a base case"
(= true (is-even? 0))
; "And starts by moving toward that base case"
(= false (is-even? 1))
; "Having too many stack frames requires explicit tail calls with recur"
(= false (is-even-bigint? 100003N))
; "Reversing directions is easy when you have not gone far"
(= '(1) (recursive-reverse [1]))
; "Yet it becomes more difficult the more steps you take"
(= '(5 4 3 2 1) (recursive-reverse [1 2 3 4 5]))
; "Simple things may appear simple."
(= 1 (factorial 1))
; "They may require other simple steps."
(= 2 (factorial 2))
; "Sometimes a slightly bigger step is necessary"
(= 6 (factorial 3))
; "And eventually you must think harder"
(= 24 (factorial 4))
; "You can even deal with very large numbers"
(< 1000000000000000000000000N (factorial 1000N))
; "But what happens when the machine limits you?"
(< 1000000000000000000000000N (factorial 100003N))

View file

@ -1,44 +1,36 @@
(ns koans.15-destructuring
(:require [koan-engine.core :refer :all]))
(def test-address ; "Destructuring is an arbiter: it breaks up arguments"
{:street-address "123 Test Lane"
:city "Testerville"
:state "TX"})
(meditations
"Destructuring is an arbiter: it breaks up arguments"
(= __ ((fn [[a b]] (str b a)) (= __ ((fn [[a b]] (str b a))
[:foo :bar])) [:foo :bar]))
"Whether in function definitions" ; "Whether in function definitions"
(= (str "An Oxford comma list of apples, " (= (str "An Oxford comma list of apples, "
"oranges, " "oranges, "
"and pears.") "and pears.")
((fn [[a b c]] __) ((fn [[a b c]] __)
["apples" "oranges" "pears"])) ["apples" "oranges" "pears"]))
"Or in let expressions" ; "Or in let expressions"
(= "Rich Hickey aka The Clojurer aka Go Time aka Lambda Guru" (= "Rich Hickey aka The Clojurer aka Go Time aka Lambda Guru"
(let [[first-name last-name & aliases] (let [[first-name last-name & aliases]
(list "Rich" "Hickey" "The Clojurer" "Go Time" "Lambda Guru")] (list "Rich" "Hickey" "The Clojurer" "Go Time" "Lambda Guru")]
__)) __))
"You can regain the full argument if you like arguing" ; "You can regain the full argument if you like arguing"
(= {:original-parts ["Stephen" "Hawking"] :named-parts {:first "Stephen" :last "Hawking"}} (= {:original-parts ["Stephen" "Hawking"] :named-parts {:first "Stephen" :last "Hawking"}}
(let [[first-name last-name :as full-name] ["Stephen" "Hawking"]] (let [[first-name last-name :as full-name] ["Stephen" "Hawking"]]
__)) __))
"Break up maps by key" ; "Break up maps by key"
(= "123 Test Lane, Testerville, TX" (= "123 Test Lane, Testerville, TX"
(let [{street-address :street-address, city :city, state :state} test-address] (let [{street-address :street-address, city :city, state :state} test-address]
__)) __))
"Or more succinctly" ; "Or more succinctly"
(= "123 Test Lane, Testerville, TX" (= "123 Test Lane, Testerville, TX"
(let [{:keys [street-address __ __]} test-address] (let [{:keys [street-address __ __]} test-address]
__)) __))
"All together now!" ; "All together now!"
(= "Test Testerson, 123 Test Lane, Testerville, TX" (= "Test Testerson, 123 Test Lane, Testerville, TX"
(___ ["Test" "Testerson"] test-address))) (___ ["Test" "Testerson"] test-address))

View file

@ -1,22 +1,16 @@
(ns koans.16-refs
(:require [koan-engine.core :refer :all]))
(def the-world (ref "hello")) ; "In the beginning, there was a word"
(def bizarro-world (ref {}))
(meditations
"In the beginning, there was a word"
(= __ (deref the-world)) (= __ (deref the-world))
"You can get the word more succinctly, but it's the same" ; "You can get the word more succinctly, but it's the same"
(= __ @the-world) (= __ @the-world)
"You can be the change you wish to see in the world." ; "You can be the change you wish to see in the world."
(= __ (do (= __ (do
(dosync (ref-set the-world "better")) (dosync (ref-set the-world "better"))
@the-world)) @the-world))
"Alter where you need not replace" ; "Alter where you need not replace"
(= __ (let [exclamator (fn [x] (str x "!"))] (= __ (let [exclamator (fn [x] (str x "!"))]
(dosync (dosync
(alter the-world exclamator) (alter the-world exclamator)
@ -24,19 +18,19 @@
(alter the-world exclamator)) (alter the-world exclamator))
@the-world)) @the-world))
"Don't forget to do your work in a transaction!" ; "Don't forget to do your work in a transaction!"
(= 0 (do __ (= 0 (do __
@the-world)) @the-world))
"Functions passed to alter may depend on the data in the ref" ; "Functions passed to alter may depend on the data in the ref"
(= 20 (do (= 20 (do
(dosync (alter the-world ___)))) (dosync (alter the-world ___))))
"Two worlds are better than one" ; "Two worlds are better than one"
(= ["Real Jerry" "Bizarro Jerry"] (= ["Real Jerry" "Bizarro Jerry"]
(do (do
(dosync (dosync
(ref-set the-world {}) (ref-set the-world {})
(alter the-world assoc :jerry "Real Jerry") (alter the-world assoc :jerry "Real Jerry")
(alter bizarro-world assoc :jerry "Bizarro Jerry") (alter bizarro-world assoc :jerry "Bizarro Jerry")
__)))) __)))

28
17_atoms.clj Normal file
View file

@ -0,0 +1,28 @@
; "Atoms are like refs"
(= __ @atomic-clock)
; "You can change at the swap meet"
(= __ (do
(swap! atomic-clock inc)
@atomic-clock))
; "Keep taxes out of this: swapping requires no transaction"
(= 5 (do
__
@atomic-clock))
; "Any number of arguments might happen during a swap"
(= __ (do
(swap! atomic-clock + 1 2 3 4 5)
@atomic-clock))
; "Atomic atoms are atomic"
(= __ (do
(compare-and-set! atomic-clock 100 :fin)
@atomic-clock))
; "When your expectations are aligned with reality, things proceed that way"
(= :fin (do
(compare-and-set! __ __ __)
@atomic-clock))

View file

@ -1,5 +1,3 @@
(ns koans.18-macros
(:require [koan-engine.core :refer :all]))
(defmacro hello [x] (defmacro hello [x]
(str "Hello, " x)) (str "Hello, " x))
@ -25,21 +23,20 @@
(r-infix ~first-arg) (r-infix ~first-arg)
(r-infix ~others))))) (r-infix ~others)))))
(meditations ; "Macros are like functions created at compile time"
"Macros are like functions created at compile time"
(= __ (hello "Macros!")) (= __ (hello "Macros!"))
"I can haz infix?" ; "I can haz infix?"
(= __ (infix (9 + 1))) (= __ (infix (9 + 1)))
"Remember, these are nothing but code transformations" ; "Remember, these are nothing but code transformations"
(= __ (macroexpand '(infix (9 + 1)))) (= __ (macroexpand '(infix (9 + 1))))
"You can do better than that - hand crafting FTW!" ; "You can do better than that - hand crafting FTW!"
(= __ (macroexpand '(infix-better (10 * 2)))) (= __ (macroexpand '(infix-better (10 * 2))))
"Things don't always work as you would like them to... " ; "Things don't always work as you would like them to... "
(= __ (macroexpand '(infix-better ( 10 + (2 * 3))))) (= __ (macroexpand '(infix-better ( 10 + (2 * 3)))))
"Really, you don't understand recursion until you understand recursion" ; "Really, you don't understand recursion until you understand recursion"
(= 36 (r-infix (10 + (2 * 3) + (4 * 5))))) (= 36 (r-infix (10 + (2 * 3) + (4 * 5))))

25
19_datatypes.clj Normal file
View file

@ -0,0 +1,25 @@
; "Holding records is meaningful only when the record is worthy of you"
(= __ (.prize (Nobel. "peace")))
; "Types are quite similar"
(= __ (.prize (Pulitzer. "literature")))
; "Records may be treated like maps"
(= __ (:prize (Nobel. "physics")))
; "While types may not"
(= __ (:prize (Pulitzer. "poetry")))
; "Further study reveals why"
(= __
(map map? [(Nobel. "chemistry")
(Pulitzer. "music")]))
; "Either sort of datatype can define methods in a protocol"
(= __
(with-out-str (present (Oscar. "Best Picture") "Evil Alien Conquerors")))
; "Surely we can implement our own by now"
(= "You're really the Worst Picture, Final Destination 5... sorry."
(with-out-str (present (Razzie. "Worst Picture") "Final Destination 5")))

16
20_java_interop.clj Normal file
View file

@ -0,0 +1,16 @@
; "You may have done more with Java than you know"
(= __ (class "warfare")) ; hint: try typing (javadoc "warfare") in the REPL
; "The dot signifies easy and direct Java interoperation"
(= __ (.toUpperCase "select * from"))
; "But instance method calls are very different from normal functions"
(= ["SELECT" "FROM" "WHERE"] (map ___ ["select" "from" "where"]))
; "Constructing might be harder than breaking"
(= 10 (let [latch (java.util.concurrent.CountDownLatch. __)]
(.getCount latch)))
; "Static methods are slashing prices!"
(== __ (Math/pow 2 10))

18
21_partition.clj Normal file
View file

@ -0,0 +1,18 @@
; "To split a collection you can use the partition function"
(= '((0 1) (2 3)) (__ 2 (range 4)))
; "But watch out if there are not enough elements to form n sequences"
(= '(__) (partition 3 [:a :b :c :d :e]))
; "You can use partition-all to include any leftovers too"
(= __ (partition-all 3 (range 5)))
; "If you need to, you can start each sequence with an offset"
(= '((0 1 2) (5 6 7) (10 11 12)) (partition 3 __ (range 13)))
; "Consider padding the last sequence with some default values..."
(= '((0 1 2) (3 4 5) (6 :hello)) (partition 3 3 [__] (range 7)))
; "... but notice that they will only pad up to the given sequence length"
(= '((0 1 2) (3 4 5) __) (partition 3 3 [:these :are "my" "words"] (range 7)))

View file

@ -1,26 +1,19 @@
(ns koans.22-group-by
(:require [koan-engine.core :refer :all]))
(defn get-odds-and-evens [coll] ; "To categorize a collection by some function, use group-by."
(let [{odds true evens false} (group-by __ coll)]
[odds evens]))
(meditations
"To categorize a collection by some function, use group-by."
(= __ (group-by count ["hello" "world" "foo" "bar"])) (= __ (group-by count ["hello" "world" "foo" "bar"]))
"You can simulate filter + remove in one pass" ; "You can simulate filter + remove in one pass"
(= (get-odds-and-evens [1 2 3 4 5]) (= (get-odds-and-evens [1 2 3 4 5])
((juxt filter remove) odd? [1 2 3 4 5]) ((juxt filter remove) odd? [1 2 3 4 5])
[[1 3 5] [2 4]]) [[1 3 5] [2 4]])
"You can also group by a primary key" ; "You can also group by a primary key"
(= __ (= __
(group-by :id [{:id 1 :name "Bob"} (group-by :id [{:id 1 :name "Bob"}
{:id 2 :name "Jennifer"} {:id 2 :name "Jennifer"}
{:id 1 :last-name "Smith"} ])) {:id 1 :last-name "Smith"} ]))
"But be careful when you group by a non-required key" ; "But be careful when you group by a non-required key"
(= {"Bob" [{:name "Bob" :id 1}] (= {"Bob" [{:name "Bob" :id 1}]
"Jennifer" [{:name "Jennifer" :id 2}] "Jennifer" [{:name "Jennifer" :id 2}]
__ [{:last-name "Smith" :id 1}]} __ [{:last-name "Smith" :id 1}]}
@ -28,9 +21,9 @@
{:id 2 :name "Jennifer"} {:id 2 :name "Jennifer"}
{:id 1 :last-name "Smith"}])) {:id 1 :last-name "Smith"}]))
"The true power of group-by comes with custom functions" ; "The true power of group-by comes with custom functions"
(= __ (= __
(group-by #(if (:bad %) :naughty-list :nice-list) (group-by #(if (:bad %) :naughty-list :nice-list)
[{:name "Jimmy" :bad true} [{:name "Jimmy" :bad true}
{:name "Jane" :bad false} {:name "Jane" :bad false}
{:name "Joe" :bad true}]))) {:name "Joe" :bad true}]))

View file

@ -1,18 +1,11 @@
(ns koans.23-meta
(:require [koan-engine.core :refer :all]))
(def giants ; "Some objects can be tagged using the with-meta function"
(with-meta 'Giants
{:league "National League"}))
(meditations
"Some objects can be tagged using the with-meta function"
(= __ (meta giants)) (= __ (meta giants))
"Or more succinctly with a reader macro" ; "Or more succinctly with a reader macro"
(= __ (meta '^{:division "West"} Giants)) (= __ (meta '^{:division "West"} Giants))
"While others can't" ; "While others can't"
(= __ (try (= __ (try
(with-meta (with-meta
2 2
@ -20,18 +13,18 @@
(catch ClassCastException e (catch ClassCastException e
"This doesn't implement the IObj interface"))) "This doesn't implement the IObj interface")))
"Notice when metadata carries over" ; "Notice when metadata carries over"
(= __ (meta (merge '^{:foo :bar} {:a 1 :b 2} (= __ (meta (merge '^{:foo :bar} {:a 1 :b 2}
{:b 3 :c 4}))) {:b 3 :c 4})))
"And when it doesn't" ; "And when it doesn't"
(= __ (meta (merge {:a 1 :b 2} (= __ (meta (merge {:a 1 :b 2}
'^{:foo :bar} {:b 3 :c 4}))) '^{:foo :bar} {:b 3 :c 4})))
"Metadata can be used as a type hint to avoid reflection during runtime" ; "Metadata can be used as a type hint to avoid reflection during runtime"
(= __ (#(.charAt ^String % 0) "Cast me")) (= __ (#(.charAt ^String % 0) "Cast me"))
"You can directly update an object's metadata" ; "You can directly update an object's metadata"
(= 8 (let [giants (= 8 (let [giants
(with-meta (with-meta
'Giants 'Giants
@ -39,13 +32,13 @@
(swap! (:world-series-titles (meta giants)) __) (swap! (:world-series-titles (meta giants)) __)
@(:world-series-titles (meta giants)))) @(:world-series-titles (meta giants))))
"You can also create a new object from another object with metadata" ; "You can also create a new object from another object with metadata"
(= {:league "National League" :park "AT&T Park"} (= {:league "National League" :park "AT&T Park"}
(meta (vary-meta giants (meta (vary-meta giants
assoc __ __))) assoc __ __)))
"But it won't affect behavior like equality" ; "But it won't affect behavior like equality"
(= __ (vary-meta giants dissoc :league)) (= __ (vary-meta giants dissoc :league))
"Or the object's printed representation" ; "Or the object's printed representation"
(= __ (pr-str (vary-meta giants dissoc :league)))) (= __ (pr-str (vary-meta giants dissoc :league)))

161
README.md
View file

@ -1,159 +1,4 @@
# Clojure Koans # clojure-koans
This repo logs my solutions for the popular Clojure Koans series and serves as a way to mark my progress as I learn the basics of Clojure.
The Clojure Koans are a fun and easy way to get started with Clojure - no There are comments in this repo that are marked with this convention: ```;; J - ```. These are simply notes and reminders that I have written to myself.
experience assumed or required. Just follow the instructions below to start
making tests pass!
### Getting Started
The easiest and fastest way to get the koans up and running is to [download the
latest zip file from Github](https://github.com/functional-koans/clojure-koans/releases).
This way, you'll have all the dependencies you need, including Clojure itself
and JLine, and you can skip the rest of this section (skip to "Running the
Koans").
If you're starting from a cloned or forked repo, that's cool too. This way
you'll be able to track your progress in Git, and see how your answers compare
to others, by checking out the project's Network tab. You might want to create
your own branch - that way if you pull back the latest koans from master, it'll
be a bit easier to manage the inevitable conflicts if we make changes to
exercises you've already completed.
The only things you'll need to run the Clojure Koans are:
- JRE 1.6 or higher
- [clojure-1.8.0.jar](http://repo1.maven.org/maven2/org/clojure/clojure/1.8.0/clojure-1.8.0.zip)
You can use [Leiningen](http://github.com/technomancy/leiningen) to
automatically install the Clojure jar in the right place. Leiningen will also
get you a couple more jarfiles, including JLine, which allows you some of the
functionality of readline (command-line history, for example).
### Installing dependencies
Dependencies are installed automatically with lein 2, but if for some reason
you're on lein 1 and can't upgrade, you'll need to run
`lein deps`
which will download all dependencies you need to run the Clojure koans.
I strongly recommend that you upgrade to lein 2 instead!
### Running the Koans
If you're running from the zipfile, simply run
`script/run` on Mac/\*nix
`script\run` on Windows
If you're running from a checkout using lein 2, run the koans via
`lein koan run`
It's an auto-runner, so as you save your files with the correct answers, it will
advance you to the next koan or file (conveniently, all files are prefixed with
the sequence that you should follow).
You'll see something like this:
Now meditate on /home/colin/Projects/clojure-koans/src/koans/01_equalities.clj:3
---------------------
Assertion failed!
We shall contemplate truth by testing reality, via equality.
(= __ true)
The output is telling you that you have a failing test in the file named
`01_equalities.clj`, on line 3. So you just need to open that file up and make
it pass! You'll always be filling in the blanks to make tests pass.
Sometimes there could be several correct answers (or even an infinite number):
any of them will work in these cases. Some tests will pass even if you replace
the blanks with whitespace (or nothing) instead of the expected answer. Make sure
you give one correct expression to replace each blank.
The koans differ from normal TDD in that the tests are already written for you,
so you'll have to pay close attention to the failure messages, because up until
the very end, making a test pass just means that the next failure message comes
up.
While it might be easy (especially at first) to just fill in the blanks making
things pass, you should work thoughtfully, making sure you understand why the
answer is what it is. Enjoy your path to Clojure enlightenment!
### Trying more things out
There's a REPL (Read-Evaluate-Print Loop) included in the Clojure Koans. Just
run:
`script/repl` on Mac/\*nix
`script\repl` on Windows
If you're on lein 2, `lein repl` is what you want instead.
Here are some interesting commands you might try, once you're in a running REPL:
```clojure
(find-doc "vec")
(find-doc #"vec$")
(doc vec)
```
And if those still don't make sense:
```clojure
(doc doc)
(doc find-doc)
```
will show you what those commands mean.
You can exit the REPL with `CTRL-d` on any OS.
### Contributing
Patches are encouraged! Make sure the answer sheet still passes
(`lein koan test`), and send a pull request.
The file ideaboard.txt has lots of good ideas for new koans to start, or things
to add to existing koans. So write some fun exercises, add your answers to
`resources/koans.clj`, and we'll get them in there!
Please follow the guidelines in
http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html for
commmit messages, and put your code in a feature branch (not master) before
making the pull request. This makes patches easier to review.
Feel free to contact me (Colin Jones / trptcolin) on Github or elsewhere if you
have any questions or want more direction before you start pitching in.
### Contributors
https://github.com/functional-koans/clojure-koans/contributors
### Credits
These exercises were started by [Aaron Bedra](http://github.com/abedra) of
[Relevance, Inc.](http://github.com/relevance) in early 2010, as a learning
tool for newcomers to functional programming. Aaron's macro-fu makes these
koans extremely simple and fun to use, and to improve upon, and without
Relevance's initiative, this project would not exist.
Using the [koans](http://en.wikipedia.org/wiki/koan) metaphor as a tool for
learning a programming language started with the
[Ruby Koans](http://rubykoans.com) by [EdgeCase](http://github.com/edgecase).
### License
The use and distribution terms for this software are covered by the
Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
which can be found in the file epl-v10.html at the root of this distribution.
By using this software in any fashion, you are agreeing to be bound by
the terms of this license.

View file

@ -1,261 +0,0 @@
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
<title>Eclipse Public License - Version 1.0</title>
<style type="text/css">
body {
size: 8.5in 11.0in;
margin: 0.25in 0.5in 0.25in 0.5in;
tab-interval: 0.5in;
}
p {
margin-left: auto;
margin-top: 0.5em;
margin-bottom: 0.5em;
}
p.list {
margin-left: 0.5in;
margin-top: 0.05em;
margin-bottom: 0.05em;
}
</style>
</head>
<body lang="EN-US">
<p align=center><b>Eclipse Public License - v 1.0</b></p>
<p>THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE
PUBLIC LICENSE (&quot;AGREEMENT&quot;). ANY USE, REPRODUCTION OR
DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS
AGREEMENT.</p>
<p><b>1. DEFINITIONS</b></p>
<p>&quot;Contribution&quot; means:</p>
<p class="list">a) in the case of the initial Contributor, the initial
code and documentation distributed under this Agreement, and</p>
<p class="list">b) in the case of each subsequent Contributor:</p>
<p class="list">i) changes to the Program, and</p>
<p class="list">ii) additions to the Program;</p>
<p class="list">where such changes and/or additions to the Program
originate from and are distributed by that particular Contributor. A
Contribution 'originates' from a Contributor if it was added to the
Program by such Contributor itself or anyone acting on such
Contributor's behalf. Contributions do not include additions to the
Program which: (i) are separate modules of software distributed in
conjunction with the Program under their own license agreement, and (ii)
are not derivative works of the Program.</p>
<p>&quot;Contributor&quot; means any person or entity that distributes
the Program.</p>
<p>&quot;Licensed Patents&quot; mean patent claims licensable by a
Contributor which are necessarily infringed by the use or sale of its
Contribution alone or when combined with the Program.</p>
<p>&quot;Program&quot; means the Contributions distributed in accordance
with this Agreement.</p>
<p>&quot;Recipient&quot; means anyone who receives the Program under
this Agreement, including all Contributors.</p>
<p><b>2. GRANT OF RIGHTS</b></p>
<p class="list">a) Subject to the terms of this Agreement, each
Contributor hereby grants Recipient a non-exclusive, worldwide,
royalty-free copyright license to reproduce, prepare derivative works
of, publicly display, publicly perform, distribute and sublicense the
Contribution of such Contributor, if any, and such derivative works, in
source code and object code form.</p>
<p class="list">b) Subject to the terms of this Agreement, each
Contributor hereby grants Recipient a non-exclusive, worldwide,
royalty-free patent license under Licensed Patents to make, use, sell,
offer to sell, import and otherwise transfer the Contribution of such
Contributor, if any, in source code and object code form. This patent
license shall apply to the combination of the Contribution and the
Program if, at the time the Contribution is added by the Contributor,
such addition of the Contribution causes such combination to be covered
by the Licensed Patents. The patent license shall not apply to any other
combinations which include the Contribution. No hardware per se is
licensed hereunder.</p>
<p class="list">c) Recipient understands that although each Contributor
grants the licenses to its Contributions set forth herein, no assurances
are provided by any Contributor that the Program does not infringe the
patent or other intellectual property rights of any other entity. Each
Contributor disclaims any liability to Recipient for claims brought by
any other entity based on infringement of intellectual property rights
or otherwise. As a condition to exercising the rights and licenses
granted hereunder, each Recipient hereby assumes sole responsibility to
secure any other intellectual property rights needed, if any. For
example, if a third party patent license is required to allow Recipient
to distribute the Program, it is Recipient's responsibility to acquire
that license before distributing the Program.</p>
<p class="list">d) Each Contributor represents that to its knowledge it
has sufficient copyright rights in its Contribution, if any, to grant
the copyright license set forth in this Agreement.</p>
<p><b>3. REQUIREMENTS</b></p>
<p>A Contributor may choose to distribute the Program in object code
form under its own license agreement, provided that:</p>
<p class="list">a) it complies with the terms and conditions of this
Agreement; and</p>
<p class="list">b) its license agreement:</p>
<p class="list">i) effectively disclaims on behalf of all Contributors
all warranties and conditions, express and implied, including warranties
or conditions of title and non-infringement, and implied warranties or
conditions of merchantability and fitness for a particular purpose;</p>
<p class="list">ii) effectively excludes on behalf of all Contributors
all liability for damages, including direct, indirect, special,
incidental and consequential damages, such as lost profits;</p>
<p class="list">iii) states that any provisions which differ from this
Agreement are offered by that Contributor alone and not by any other
party; and</p>
<p class="list">iv) states that source code for the Program is available
from such Contributor, and informs licensees how to obtain it in a
reasonable manner on or through a medium customarily used for software
exchange.</p>
<p>When the Program is made available in source code form:</p>
<p class="list">a) it must be made available under this Agreement; and</p>
<p class="list">b) a copy of this Agreement must be included with each
copy of the Program.</p>
<p>Contributors may not remove or alter any copyright notices contained
within the Program.</p>
<p>Each Contributor must identify itself as the originator of its
Contribution, if any, in a manner that reasonably allows subsequent
Recipients to identify the originator of the Contribution.</p>
<p><b>4. COMMERCIAL DISTRIBUTION</b></p>
<p>Commercial distributors of software may accept certain
responsibilities with respect to end users, business partners and the
like. While this license is intended to facilitate the commercial use of
the Program, the Contributor who includes the Program in a commercial
product offering should do so in a manner which does not create
potential liability for other Contributors. Therefore, if a Contributor
includes the Program in a commercial product offering, such Contributor
(&quot;Commercial Contributor&quot;) hereby agrees to defend and
indemnify every other Contributor (&quot;Indemnified Contributor&quot;)
against any losses, damages and costs (collectively &quot;Losses&quot;)
arising from claims, lawsuits and other legal actions brought by a third
party against the Indemnified Contributor to the extent caused by the
acts or omissions of such Commercial Contributor in connection with its
distribution of the Program in a commercial product offering. The
obligations in this section do not apply to any claims or Losses
relating to any actual or alleged intellectual property infringement. In
order to qualify, an Indemnified Contributor must: a) promptly notify
the Commercial Contributor in writing of such claim, and b) allow the
Commercial Contributor to control, and cooperate with the Commercial
Contributor in, the defense and any related settlement negotiations. The
Indemnified Contributor may participate in any such claim at its own
expense.</p>
<p>For example, a Contributor might include the Program in a commercial
product offering, Product X. That Contributor is then a Commercial
Contributor. If that Commercial Contributor then makes performance
claims, or offers warranties related to Product X, those performance
claims and warranties are such Commercial Contributor's responsibility
alone. Under this section, the Commercial Contributor would have to
defend claims against the other Contributors related to those
performance claims and warranties, and if a court requires any other
Contributor to pay any damages as a result, the Commercial Contributor
must pay those damages.</p>
<p><b>5. NO WARRANTY</b></p>
<p>EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS
PROVIDED ON AN &quot;AS IS&quot; BASIS, WITHOUT WARRANTIES OR CONDITIONS
OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION,
ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY
OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely
responsible for determining the appropriateness of using and
distributing the Program and assumes all risks associated with its
exercise of rights under this Agreement , including but not limited to
the risks and costs of program errors, compliance with applicable laws,
damage to or loss of data, programs or equipment, and unavailability or
interruption of operations.</p>
<p><b>6. DISCLAIMER OF LIABILITY</b></p>
<p>EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT
NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING
WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR
DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.</p>
<p><b>7. GENERAL</b></p>
<p>If any provision of this Agreement is invalid or unenforceable under
applicable law, it shall not affect the validity or enforceability of
the remainder of the terms of this Agreement, and without further action
by the parties hereto, such provision shall be reformed to the minimum
extent necessary to make such provision valid and enforceable.</p>
<p>If Recipient institutes patent litigation against any entity
(including a cross-claim or counterclaim in a lawsuit) alleging that the
Program itself (excluding combinations of the Program with other
software or hardware) infringes such Recipient's patent(s), then such
Recipient's rights granted under Section 2(b) shall terminate as of the
date such litigation is filed.</p>
<p>All Recipient's rights under this Agreement shall terminate if it
fails to comply with any of the material terms or conditions of this
Agreement and does not cure such failure in a reasonable period of time
after becoming aware of such noncompliance. If all Recipient's rights
under this Agreement terminate, Recipient agrees to cease use and
distribution of the Program as soon as reasonably practicable. However,
Recipient's obligations under this Agreement and any licenses granted by
Recipient relating to the Program shall continue and survive.</p>
<p>Everyone is permitted to copy and distribute copies of this
Agreement, but in order to avoid inconsistency the Agreement is
copyrighted and may only be modified in the following manner. The
Agreement Steward reserves the right to publish new versions (including
revisions) of this Agreement from time to time. No one other than the
Agreement Steward has the right to modify this Agreement. The Eclipse
Foundation is the initial Agreement Steward. The Eclipse Foundation may
assign the responsibility to serve as the Agreement Steward to a
suitable separate entity. Each new version of the Agreement will be
given a distinguishing version number. The Program (including
Contributions) may always be distributed subject to the version of the
Agreement under which it was received. In addition, after a new version
of the Agreement is published, Contributor may elect to distribute the
Program (including its Contributions) under the new version. Except as
expressly stated in Sections 2(a) and 2(b) above, Recipient receives no
rights or licenses to the intellectual property of any Contributor under
this Agreement, whether expressly, by implication, estoppel or
otherwise. All rights in the Program not expressly granted under this
Agreement are reserved.</p>
<p>This Agreement is governed by the laws of the State of New York and
the intellectual property laws of the United States of America. No party
to this Agreement will bring a legal action under this Agreement more
than one year after the cause of action arose. Each party waives its
rights to a jury trial in any resulting litigation.</p>
</body>
</html>

View file

@ -1,31 +0,0 @@
Concepts / Language Features
=====
Quoting
new record syntax
Agents
Vars
state identity lifetime
immutability / side effects
type hints
Pre and Post conditions of functions
ex-info/ex-data
reducers (?)
transducers (?) - maybe just some basic ones, nothing too crazy
Particular Functions
=====
fnil - creating_a_function
juxt - creating_a_function
constantly - creating_a_function
flatten
frequencies
reductions
keep
keep-indexed
map-indexed
partition-all
partition-by
repeatedly

View file

@ -1,10 +0,0 @@
(defproject clojure-koans "0.5.1-SNAPSHOT"
:description "The Clojure koans."
:dependencies [[org.clojure/clojure "1.8.0"]
[koan-engine "0.2.3"]]
:dev-dependencies [[lein-koan "0.1.3"]]
:profiles {:dev {:dependencies [[lein-koan "0.1.3"]]}}
:repl-options {:init-ns koan-engine.runner
:init (do (use '[koan-engine.core]))}
:plugins [[lein-koan "0.1.3"]]
:main koan-engine.runner/exec)

View file

@ -1,256 +0,0 @@
[["01_equalities" {"__" [true
2
5
true
false
true
true
false
"hello"
"hello"
3]}]
["02_strings" {"__" ["hello"
"world"
"Cool "
"right?"
0
11
false
6 11
"123"
", "
"1" "2" "3"
"olleh"
"hello"
13
nil
"hello world"
true
false
false
"a"
true
true
false]}]
["03_lists" {"__" [1 2 3 4 5
1
[2 3 4 5]
3
0
()
[:a :b :c :d :e]
[:e :a :b :c :d]
:a
[:b :c :d :e]
"No dice!"
()]}]
["04_vectors" {"__" [1
[1]
[nil nil]
2
[111 222 333]
:peanut
:jelly
:jelly
[:butter :and]
3]}]
["05_sets" {"__" [[3]
3
#{1 2 3 4 5}
#{1 2 3 4 5}
#{2 3}
#{1 4}]}]
["06_maps" {"__" [:b 2
1
2
2
1
1
"Sochi"
nil
:key-not-found
true
false
"February"
1 "January"
2010 2014 2018
"PyeongChang" "Sochi" "Vancouver"]}]
["07_functions" {"__" [81
20
10
60
15
30]
"___" [+
*
(fn [f] (f 5))
(fn [f] (f 5))]}]
["08_conditionals" {"__" [:a
[]
nil
:glory
4 6 :your-road
1
:bicycling
"is that even exercise?"]}]
["09_higher_order_functions" {"__" [4 8 12
(* x x)
[false false true false false]
()
[:anything :goes :here]
(< x 31)
(* 10 x) (< x 4)
24
100
(count a) (count b)]}]
["10_runtime_polymorphism" {"__" [(str (:name a) " eats veggies.")
(str (:name a) " eats animals.")
(str "I don't know what " (:name a) " eats.")
"Hello World!"
"Hello, you silly world."
"Hello to this group: Peter, Paul, Mary!" ]}]
["11_lazy_sequences" {"__" [[1 2 3 4]
[0 1 2 3 4]
10
95
(range 20)
:a]
"___" [(fn [x] x)]}]
["12_sequence_comprehensions" {"__" [[0 1 2 3 4 5]
(* x x)
(range 10)
(odd? x) (* x x)
[row column]
]}]
["13_creating_functions" {"__" [true false true
4
:a :b :c :d
:c :d
4
8]
"___" [(complement nil?)
multiply-by-5
(comp dec square)]}]
["14_recursion" {"__" [true
acc
(loop [coll coll
acc ()]
(if (seq coll)
(recur (rest coll) (conj acc (first coll)))
acc))
(loop [n n
acc 1]
(if (zero? n)
acc
(recur (dec n) (* acc n))))]
"___" [not]}]
["15_destructuring" {"__" [":bar:foo"
(format (str "An Oxford comma list of %s, "
"%s, "
"and %s.")
a b c)
(apply str
(interpose " "
(apply list
first-name
last-name
(interleave (repeat "aka") aliases))))
{:original-parts full-name
:named-parts {:first first-name :last last-name}}
(str street-address ", " city ", " state)
city state
(str street-address ", " city ", " state)]
"___" [(fn [[fname lname]
{:keys [street-address city state]}]
(str fname " " lname ", "
street-address ", " city ", " state))
]}]
["16_refs" {"__" ["hello"
"hello"
"better"
"better!!!"
(dosync (ref-set the-world 0))
(map :jerry [@the-world @bizarro-world])
]
"___" [(fn [x] (+ 20 x))]}]
["17_atoms" {"__" [0
1
(swap! atomic-clock (partial + 4))
20
20
atomic-clock 20 :fin
]}]
["18_macros" {"__" [~(first form)
~(nth form 2)
form
(drop 2 form)
"Hello, Macros!"
10
'(+ 9 1)
'(* 10 2)
'(+ 10 (2 * 3))]}]
["19_datatypes" {"__" [(print
(str "You're really the "
(.category this)
", " recipient "... sorry."))
"peace"
"literature"
"physics"
nil
[true false]
(str "Congratulations on your Best Picture Oscar, "
"Evil Alien Conquerors!")]}]
["20_java_interop" {"__" [java.lang.String
"SELECT * FROM"
10
1024
]
"___" [#(.toUpperCase %)
]
}]
["21_partition" {"__" [partition
[:a :b :c]
'((0 1 2) (3 4))
5
:hello
(6 :these :are)
]}]
["22_group_by" {"__" [odd?
{5 ["hello" "world"] 3 ["foo" "bar"]}
{1 [{:name "Bob" :id 1}
{:last-name "Smith" :id 1}]
2 [{:name "Jennifer" :id 2}]}
nil
{:naughty-list [{:name "Jimmy" :bad true}
{:name "Joe" :bad true}]
:nice-list [{:name "Jane" :bad false}]}]}]
["23_meta" {"__" [{:league "National League"}
{:division "West"}
"This doesn't implement the IObj interface"
{:foo :bar}
nil
\C
inc
:park "AT&T Park"
'Giants
"Giants"]}]
]

View file

@ -1,18 +0,0 @@
#!/bin/sh
mkdir -p releases
lein1 deps
zip -r releases/clojure-koans-`date +"%Y-%m-%d_%H-%M"`.zip \
. \
-x "./.idea/*" \
-x "./.lein-plugins/*" \
-x "./.git/*" \
-x "releases/*"
echo
echo "Don't forget to upload the zipfile (somewhere...)"
echo `ls -t releases/clojure-koans-*.zip | head -n1`
echo "git push"
echo "git push --tags"
echo

View file

@ -1,12 +0,0 @@
#!/bin/sh
CLASSPATH=src
for f in lib/*.jar lib/dev/*.jar resources/; do
CLASSPATH=$CLASSPATH:$f
done
if [ "$OSTYPE" = "cygwin" ]; then
CLASSPATH=`cygpath -wp $CLASSPATH`
fi
java -Xmx1G -cp $CLASSPATH jline.ConsoleRunner clojure.main

View file

@ -1,14 +0,0 @@
@echo off
setLocal EnableDelayedExpansion
set CLASSPATH="
for %%j in (".\lib\*.jar") do (
set CLASSPATH=!CLASSPATH!;%%~fj
)
set CLASSPATH=!CLASSPATH!"
set CLASSPATH=%CLASSPATH%;src;resources
set JLINE=jline.ConsoleRunner
java -Xmx1G -cp %CLASSPATH% %JLINE% clojure.main

View file

@ -1,13 +0,0 @@
#!/bin/sh
CLASSPATH=src
for f in lib/*.jar lib/dev/*.jar resources/; do
CLASSPATH=$CLASSPATH:$f
done
if [ "$OSTYPE" = "cygwin" ]; then
CLASSPATH=`cygpath -wp $CLASSPATH`
fi
java -cp "$CLASSPATH" clojure.main script/run.clj
echo

View file

@ -1,12 +0,0 @@
@echo off
setLocal EnableDelayedExpansion
set CLASSPATH="
for %%j in (".\lib\*.jar", ".\lib\dev\*.jar") do (
set CLASSPATH=!CLASSPATH!;%%~fj
)
set CLASSPATH=!CLASSPATH!"
set CLASSPATH=%CLASSPATH%;src;resources
java -Xmx1G -cp %CLASSPATH% clojure.main script\run.clj

View file

@ -1,2 +0,0 @@
(require 'koan-engine.runner)
(koan-engine.runner/exec "run")

View file

@ -1,14 +0,0 @@
#!/bin/sh
CLASSPATH=src
for f in lib/*.jar lib/dev/*.jar resources/; do
CLASSPATH=$CLASSPATH:$f
done
if [ "$OSTYPE" = "cygwin" ]; then
CLASSPATH=`cygpath -wp $CLASSPATH`
fi
java -cp "$CLASSPATH" clojure.main script/test.clj
echo

View file

@ -1,11 +0,0 @@
@echo off
setLocal EnableDelayedExpansion
set CLASSPATH="
for %%j in (".\lib\*.jar", ".\lib\dev\*.jar") do (
set CLASSPATH=!CLASSPATH!;%%~fj
)
set CLASSPATH=!CLASSPATH!"
set CLASSPATH=%CLASSPATH%;src;resources
java -Xmx1G -cp %CLASSPATH% clojure.main script\test.clj

View file

@ -1,2 +0,0 @@
(require 'koan-engine.runner)
(koan-engine.runner/exec "test")

View file

@ -1,36 +0,0 @@
(ns koans.01-equalities
(:require [koan-engine.core :refer :all]))
(meditations
"We shall contemplate truth by testing reality, via equality"
(= __ true)
"To understand reality, we must compare our expectations against reality"
(= __ (+ 1 1))
"You can test equality of many things"
(= (+ 3 4) 7 (+ 2 __))
"Some things may appear different, but be the same"
(= __ (= 2 2/1))
"You cannot generally float to heavens of integers"
(= __ (= 2 2.0))
"But a looser equality is also possible"
(= __ (== 2.0 2))
"Something is not equal to nothing"
(= __ (not (= 1 nil)))
"Strings, and keywords, and symbols: oh my!"
(= __ (= "hello" :hello 'hello))
"Make a keyword with your keyboard"
(= :hello (keyword __))
"Symbolism is all around us"
(= 'hello (symbol __))
"When things cannot be equal, they must be different"
(not= :fill-in-the-blank __))

View file

@ -1,70 +0,0 @@
(ns koans.02-strings
(:require [koan-engine.core :refer :all]
[clojure.string :as string]))
(meditations
"A string is nothing more than text surrounded by double quotes"
(= __ "hello")
"But double quotes are just magic on top of something deeper"
(= __ (str 'world))
"You can do more than create strings, you can put them together"
(= "Cool right?" (str __ __))
"You can even get certain characters"
(= \C (get "Characters" __))
"Or even count the characters"
(= __ (count "Hello World"))
"But strings and characters are not the same"
(= __ (= \c "c"))
"What if you only wanted to get part of a string?"
(= "World" (subs "Hello World" __ __))
"How about joining together elements in a list?"
(= __ (string/join '(1 2 3)))
"What if you wanted to separate them out?"
(= "1, 2, 3" (string/join __ '(1 2 3)))
"Maybe you want to separate out all your lines"
(= [__ __ __] (string/split-lines "1\n2\n3"))
"You may want to make sure your words are backwards"
(= __ (string/reverse "hello"))
"Maybe you want to find the index of the first occurence of a substring"
(= 0 (string/index-of "hello world" __))
"Or maybe the last index of the same"
(= __ (string/last-index-of "hello world, hello" "hello"))
"But when something doesn't exist, nothing is found"
(= __ (string/index-of "hello world" "bob"))
"Sometimes you don't want whitespace cluttering the front and back"
(= __ (string/trim " \nhello world \t \n"))
"You can check if something is a char"
(= __ (char? \c))
"But it may not be"
(= __ (char? "a"))
"But chars aren't strings"
(= __ (string? \b))
"Strings are strings"
(= true (string? __))
"Some strings may be blank"
(= __ (string/blank? ""))
"Even if at first glance they aren't"
(= __ (string/blank? " \n \t "))
"However, most strings aren't blank"
(= __ (string/blank? "hello?\nare you out there?")))

View file

@ -1,45 +0,0 @@
(ns koans.03-lists
(:require [koan-engine.core :refer :all]))
(meditations
"Lists can be expressed by function or a quoted form"
(= '(__ __ __ __ __) (list 1 2 3 4 5))
"They are Clojure seqs (sequences), so they allow access to the first"
(= __ (first '(1 2 3 4 5)))
"As well as the rest"
(= __ (rest '(1 2 3 4 5)))
"Count your blessings"
(= __ (count '(dracula dooku chocula)))
"Before they are gone"
(= __ (count '()))
"The rest, when nothing is left, is empty"
(= __ (rest '(100)))
"Construction by adding an element to the front is easy"
(= __ (cons :a '(:b :c :d :e)))
"Conjoining an element to a list isn't hard either"
(= __ (conj '(:a :b :c :d) :e))
"You can use a list like a stack to get the first element"
(= __ (peek '(:a :b :c :d :e)))
"Or the others"
(= __ (pop '(:a :b :c :d :e)))
"But watch out if you try to pop nothing"
(= __ (try
(pop '())
(catch IllegalStateException e
"No dice!")))
"The rest of nothing isn't so strict"
(= __ (try
(rest '())
(catch IllegalStateException e
"No dice!"))))

View file

@ -1,33 +0,0 @@
(ns koans.04-vectors
(:require [koan-engine.core :refer :all]))
(meditations
"You can use vectors in clojure as array-like structures"
(= __ (count [42]))
"You can create a vector from a list"
(= __ (vec '(1)))
"Or from some elements"
(= __ (vector nil nil))
"But you can populate it with any number of elements at once"
(= [1 __] (vec '(1 2)))
"Conjoining to a vector is different than to a list"
(= __ (conj [111 222] 333))
"You can get the first element of a vector like so"
(= __ (first [:peanut :butter :and :jelly]))
"And the last in a similar fashion"
(= __ (last [:peanut :butter :and :jelly]))
"Or any index if you wish"
(= __ (nth [:peanut :butter :and :jelly] 3))
"You can also slice a vector"
(= __ (subvec [:peanut :butter :and :jelly] 1 3))
"Equality with collections is in terms of values"
(= (list 1 2 3) (vector 1 2 __)))

View file

@ -1,22 +0,0 @@
(ns koans.05-sets
(:require [koan-engine.core :refer :all]
[clojure.set :as set]))
(meditations
"You can create a set by converting another collection"
(= #{3} (set __))
"Counting them is like counting other collections"
(= __ (count #{1 2 3}))
"Remember that a set is a *mathematical* set"
(= __ (set '(1 1 2 2 3 3 4 4 5 5)))
"You can ask clojure for the union of two sets"
(= __ (set/union #{1 2 3 4} #{2 3 5}))
"And also the intersection"
(= __ (set/intersection #{1 2 3 4} #{2 3 5}))
"But don't forget about the difference"
(= __ (set/difference #{1 2 3 4 5} #{2 3 5})))

View file

@ -1,50 +0,0 @@
(ns koans.06-maps
(:require [koan-engine.core :refer :all]))
(meditations
"Don't get lost when creating a map"
(= {:a 1 :b 2} (hash-map :a 1 __ __))
"A value must be supplied for each key"
(= {:a 1} (hash-map :a __))
"The size is the number of entries"
(= __ (count {:a 1 :b 2}))
"You can look up the value for a given key"
(= __ (get {:a 1 :b 2} :b))
"Maps can be used as functions to do lookups"
(= __ ({:a 1 :b 2} :a))
"And so can keywords"
(= __ (:a {:a 1 :b 2}))
"But map keys need not be keywords"
(= __ ({2010 "Vancouver" 2014 "Sochi" 2018 "PyeongChang"} 2014))
"You may not be able to find an entry for a key"
(= __ (get {:a 1 :b 2} :c))
"But you can provide your own default"
(= __ (get {:a 1 :b 2} :c :key-not-found))
"You can find out if a key is present"
(= __ (contains? {:a nil :b nil} :b))
"Or if it is missing"
(= __ (contains? {:a nil :b nil} :c))
"Maps are immutable, but you can create a new and improved version"
(= {1 "January" 2 __} (assoc {1 "January"} 2 "February"))
"You can also create a new version with an entry removed"
(= {__ __} (dissoc {1 "January" 2 "February"} 2))
"Often you will need to get the keys, but the order is undependable"
(= (list __ __ __)
(sort (keys { 2014 "Sochi" 2018 "PyeongChang" 2010 "Vancouver"})))
"You can get the values in a similar way"
(= (list __ __ __)
(sort (vals {2010 "Vancouver" 2014 "Sochi" 2018 "PyeongChang"}))))

View file

@ -1,40 +0,0 @@
(ns koans.07-functions
(:require [koan-engine.core :refer :all]))
(defn multiply-by-ten [n]
(* 10 n))
(defn square [n] (* n n))
(meditations
"Calling a function is like giving it a hug with parentheses"
(= __ (square 9))
"Functions are usually defined before they are used"
(= __ (multiply-by-ten 2))
"But they can also be defined inline"
(= __ ((fn [n] (* 5 n)) 2))
"Or using an even shorter syntax"
(= __ (#(* 15 %) 4))
"Even anonymous functions may take multiple arguments"
(= __ (#(+ %1 %2 %3) 4 5 6))
"Arguments can also be skipped"
(= __ (#(* 15 %2) 1 2))
"One function can beget another"
(= 9 (((fn [] ___)) 4 5))
"Functions can also take other functions as input"
(= 20 ((fn [f] (f 4 5))
___))
"Higher-order functions take function arguments"
(= 25 (___
(fn [n] (* n n))))
"But they are often better written using the names of functions"
(= 25 (___ square)))

View file

@ -1,47 +0,0 @@
(ns koans.08-conditionals
(:require [koan-engine.core :refer :all]))
(defn explain-exercise-velocity [exercise-term]
(case exercise-term
:bicycling "pretty fast"
:jogging "not super fast"
:walking "not fast at all"
"is that even exercise?"))
(meditations
"You will face many decisions"
(= __ (if (false? (= 4 5))
:a
:b))
"Some of them leave you no alternative"
(= __ (if (> 4 3)
[]))
"And in such a situation you may have nothing"
(= __ (if (nil? 0)
[:a :b :c]))
"In others your alternative may be interesting"
(= :glory (if (not (empty? ()))
:doom
__))
"You may have a multitude of possible paths"
(let [x 5]
(= :your-road (cond (= x __) :road-not-taken
(= x __) :another-road-not-taken
:else __)))
"Or your fate may be sealed"
(= 'doom (if-not (zero? __)
'doom
'more-doom))
"In case of emergency, go fast"
(= "pretty fast"
(explain-exercise-velocity __))
"But admit it when you don't know what to do"
(= __
(explain-exercise-velocity :watching-tv)))

View file

@ -1,35 +0,0 @@
(ns koans.09-higher-order-functions
(:require [koan-engine.core :refer :all]))
(meditations
"The map function relates a sequence to another"
(= [__ __ __] (map (fn [x] (* 4 x)) [1 2 3]))
"You may create that mapping"
(= [1 4 9 16 25] (map (fn [x] __) [1 2 3 4 5]))
"Or use the names of existing functions"
(= __ (map nil? [:a :b nil :c :d]))
"A filter can be strong"
(= __ (filter (fn [x] false) '(:anything :goes :here)))
"Or very weak"
(= __ (filter (fn [x] true) '(:anything :goes :here)))
"Or somewhere in between"
(= [10 20 30] (filter (fn [x] __) [10 20 30 40 50 60 70 80]))
"Maps and filters may be combined"
(= [10 20 30] (map (fn [x] __) (filter (fn [x] __) [1 2 3 4 5 6 7 8])))
"Reducing can increase the result"
(= __ (reduce (fn [a b] (* a b)) [1 2 3 4]))
"You can start somewhere else"
(= 2400 (reduce (fn [a b] (* a b)) __ [1 2 3 4]))
"Numbers are not the only things one can reduce"
(= "longest" (reduce (fn [a b]
(if (< __ __) b a))
["which" "word" "is" "longest"])))

View file

@ -1,42 +0,0 @@
(ns koans.10-runtime-polymorphism
(:require [koan-engine.core :refer :all]))
(defn hello
([] "Hello World!")
([a] (str "Hello, you silly " a "."))
([a & more] (str "Hello to this group: "
(apply str
(interpose ", " (cons a more)))
"!")))
(defmulti diet (fn [x] (:eater x)))
(defmethod diet :herbivore [a] __)
(defmethod diet :carnivore [a] __)
(defmethod diet :default [a] __)
(meditations
"Some functions can be used in different ways - with no arguments"
(= __ (hello))
"With one argument"
(= __ (hello "world"))
"Or with many arguments"
(= __
(hello "Peter" "Paul" "Mary"))
"Multimethods allow more complex dispatching"
(= "Bambi eats veggies."
(diet {:species "deer" :name "Bambi" :age 1 :eater :herbivore}))
"Animals have different names"
(= "Thumper eats veggies."
(diet {:species "rabbit" :name "Thumper" :age 1 :eater :herbivore}))
"Different methods are used depending on the dispatch function result"
(= "Simba eats animals."
(diet {:species "lion" :name "Simba" :age 1 :eater :carnivore}))
"You may use a default method when no others match"
(= "I don't know what Rich Hickey eats."
(diet {:name "Rich Hickey"})))

View file

@ -1,28 +0,0 @@
(ns koans.11-lazy-sequences
(:require [koan-engine.core :refer :all]))
(meditations
"There are many ways to generate a sequence"
(= __ (range 1 5))
"The range starts at the beginning by default"
(= __ (range 5))
"Only take what you need when the sequence is large"
(= [0 1 2 3 4 5 6 7 8 9]
(take __ (range 100)))
"Or limit results by dropping what you don't need"
(= [95 96 97 98 99]
(drop __ (range 100)))
"Iteration provides an infinite lazy sequence"
(= __ (take 20 (iterate inc 0)))
"Repetition is key"
(= [:a :a :a :a :a :a :a :a :a :a]
(repeat 10 __))
"Iteration can be used for repetition"
(= (repeat 100 :hello)
(take 100 (iterate ___ :hello))))

View file

@ -1,54 +0,0 @@
(ns koans.14-recursion
(:require [koan-engine.core :refer :all]))
(defn is-even? [n]
(if (= n 0)
__
(___ (is-even? (dec n)))))
(defn is-even-bigint? [n]
(loop [n n
acc true]
(if (= n 0)
__
(recur (dec n) (not acc)))))
(defn recursive-reverse [coll]
__)
(defn factorial [n]
__)
(meditations
"Recursion ends with a base case"
(= true (is-even? 0))
"And starts by moving toward that base case"
(= false (is-even? 1))
"Having too many stack frames requires explicit tail calls with recur"
(= false (is-even-bigint? 100003N))
"Reversing directions is easy when you have not gone far"
(= '(1) (recursive-reverse [1]))
"Yet it becomes more difficult the more steps you take"
(= '(5 4 3 2 1) (recursive-reverse [1 2 3 4 5]))
"Simple things may appear simple."
(= 1 (factorial 1))
"They may require other simple steps."
(= 2 (factorial 2))
"Sometimes a slightly bigger step is necessary"
(= 6 (factorial 3))
"And eventually you must think harder"
(= 24 (factorial 4))
"You can even deal with very large numbers"
(< 1000000000000000000000000N (factorial 1000N))
"But what happens when the machine limits you?"
(< 1000000000000000000000000N (factorial 100003N)))

View file

@ -1,33 +0,0 @@
(ns koans.17-atoms
(:require [koan-engine.core :refer :all]))
(def atomic-clock (atom 0))
(meditations
"Atoms are like refs"
(= __ @atomic-clock)
"You can change at the swap meet"
(= __ (do
(swap! atomic-clock inc)
@atomic-clock))
"Keep taxes out of this: swapping requires no transaction"
(= 5 (do
__
@atomic-clock))
"Any number of arguments might happen during a swap"
(= __ (do
(swap! atomic-clock + 1 2 3 4 5)
@atomic-clock))
"Atomic atoms are atomic"
(= __ (do
(compare-and-set! atomic-clock 100 :fin)
@atomic-clock))
"When your expectations are aligned with reality, things proceed that way"
(= :fin (do
(compare-and-set! __ __ __)
@atomic-clock)))

View file

@ -1,47 +0,0 @@
(ns koans.19-datatypes
(:require [koan-engine.core :refer :all]))
(defrecord Nobel [prize])
(deftype Pulitzer [prize])
(defprotocol Award
(present [this recipient]))
(defrecord Oscar [category]
Award
(present [this recipient]
(print (str "Congratulations on your "
(:category this) " Oscar, "
recipient
"!"))))
(deftype Razzie [category]
Award
(present [this recipient]
__))
(meditations
"Holding records is meaningful only when the record is worthy of you"
(= __ (.prize (Nobel. "peace")))
"Types are quite similar"
(= __ (.prize (Pulitzer. "literature")))
"Records may be treated like maps"
(= __ (:prize (Nobel. "physics")))
"While types may not"
(= __ (:prize (Pulitzer. "poetry")))
"Further study reveals why"
(= __
(map map? [(Nobel. "chemistry")
(Pulitzer. "music")]))
"Either sort of datatype can define methods in a protocol"
(= __
(with-out-str (present (Oscar. "Best Picture") "Evil Alien Conquerors")))
"Surely we can implement our own by now"
(= "You're really the Worst Picture, Final Destination 5... sorry."
(with-out-str (present (Razzie. "Worst Picture") "Final Destination 5"))))

View file

@ -1,19 +0,0 @@
(ns koans.20-java-interop
(:require [koan-engine.core :refer :all]))
(meditations
"You may have done more with Java than you know"
(= __ (class "warfare")) ; hint: try typing (javadoc "warfare") in the REPL
"The dot signifies easy and direct Java interoperation"
(= __ (.toUpperCase "select * from"))
"But instance method calls are very different from normal functions"
(= ["SELECT" "FROM" "WHERE"] (map ___ ["select" "from" "where"]))
"Constructing might be harder than breaking"
(= 10 (let [latch (java.util.concurrent.CountDownLatch. __)]
(.getCount latch)))
"Static methods are slashing prices!"
(== __ (Math/pow 2 10)))

View file

@ -1,21 +0,0 @@
(ns koans.21-partition
(:require [koan-engine.core :refer :all]))
(meditations
"To split a collection you can use the partition function"
(= '((0 1) (2 3)) (__ 2 (range 4)))
"But watch out if there are not enough elements to form n sequences"
(= '(__) (partition 3 [:a :b :c :d :e]))
"You can use partition-all to include any leftovers too"
(= __ (partition-all 3 (range 5)))
"If you need to, you can start each sequence with an offset"
(= '((0 1 2) (5 6 7) (10 11 12)) (partition 3 __ (range 13)))
"Consider padding the last sequence with some default values..."
(= '((0 1 2) (3 4 5) (6 :hello)) (partition 3 3 [__] (range 7)))
"... but notice that they will only pad up to the given sequence length"
(= '((0 1 2) (3 4 5) __) (partition 3 3 [:these :are "my" "words"] (range 7))))