├── clock ├── deps.edn ├── project.clj ├── README.md ├── src │ ├── clock.clj │ └── design_journal.clj └── test │ └── clock_test.clj ├── space-age ├── deps.edn ├── project.clj ├── src │ ├── space_age.clj │ ├── space_age_pure.clj │ └── design_journal_redux.clj ├── README.md └── test │ ├── space_age_test.clj │ └── space_age_pure_test.clj ├── series ├── deps.edn ├── src │ ├── series.clj │ └── design_journal.clj ├── test │ └── series_test.clj └── README.md ├── spiral-matrix ├── deps.edn ├── project.clj ├── README.md ├── test │ └── spiral_matrix_test.clj └── src │ ├── spiral_matrix.clj │ └── design_journal.clj ├── two-fer ├── deps.edn ├── src │ ├── two_fer.clj │ └── design_journal.clj ├── project.clj ├── mentor-notes.md ├── test │ └── two_fer_test.clj └── README.md ├── bank-account ├── deps.edn ├── README.md ├── src │ ├── bank_account.clj │ └── design_journal.clj └── test │ └── bank_account_test.clj ├── armstrong-numbers ├── deps.edn ├── project.clj ├── README.md ├── test │ └── armstrong_numbers_test.clj ├── mentor-feedback.md └── src │ └── armstrong_numbers.clj ├── etl ├── deps.edn ├── src │ └── etl.clj ├── test │ └── etl_test.clj └── README.md ├── hamming ├── deps.edn ├── test │ └── hamming_test.clj ├── README.md └── src │ ├── mentor_journal.clj │ ├── design_journal.clj │ └── hamming.clj ├── pangram ├── deps.edn ├── src │ ├── pangram.clj │ └── mentor_journal.clj ├── README.md └── test │ └── pangram_test.clj ├── pov ├── deps.edn ├── src │ ├── pov.clj │ └── design_journal.clj ├── README.md └── test │ └── pov_test.clj ├── gigasecond ├── deps.edn ├── README.md ├── test │ └── gigasecond_test.clj └── src │ ├── gigasecond.clj │ └── design_journal.clj ├── phone-number ├── deps.edn ├── src │ └── phone_number.clj ├── test │ └── phone_number_test.clj └── README.md ├── word-count ├── deps.edn ├── src │ ├── word_count.clj │ └── design_journal.clj ├── README.md └── test │ └── word_count_test.clj ├── isbn-verifier ├── deps.edn ├── src │ ├── isbn_verifier.clj │ └── design_journal.clj ├── test │ └── isbn_verifier_test.clj └── README.md ├── rna-transcription ├── deps.edn ├── test │ ├── rna_transcription_test.clj │ └── rna_transcription_pure_test.clj ├── README.md └── src │ ├── rna-transcription-pure.clj │ ├── rna_transcription.clj │ ├── design_journal_redux.clj │ └── design_journal.clj ├── bob ├── deps.edn ├── project.clj ├── README.md ├── src │ └── bob.clj └── test │ └── bob_test.clj ├── collatz-conjecture ├── deps.edn ├── src │ ├── collatz_conjecture.clj │ └── design_journal.clj ├── test │ └── collatz_conjecture_test.clj └── README.md ├── run-length-encoding ├── deps.edn ├── src │ └── run_length_encoding.clj ├── README.md └── test │ └── run_length_encoding_test.clj ├── hello-world ├── src │ └── hello_world.clj ├── test │ └── hello_world_test.clj ├── project.clj └── README.md ├── .gitignore ├── .dir-locals.el ├── anagram ├── project.clj ├── src │ ├── anagram.clj │ └── design_journal.clj ├── README.md ├── test │ └── anagram_test.clj └── HELP.md ├── beer-song ├── project.clj ├── test │ └── beer_song_test.clj └── src │ └── beer_song.clj ├── reverse-string ├── project.clj ├── README.md ├── src │ └── reverse_string.clj └── test │ └── reverse_string_test.clj ├── nucleotide-count ├── project.clj ├── README.md ├── test │ └── nucleotide_count_test.clj └── src │ ├── design_journal.clj │ └── nucleotide_count.clj └── README.md /clock/deps.edn: -------------------------------------------------------------------------------- 1 | {:path 2 | ["src" "test"]} 3 | -------------------------------------------------------------------------------- /space-age/deps.edn: -------------------------------------------------------------------------------- 1 | {:paths 2 | ["src" "test"]} 3 | -------------------------------------------------------------------------------- /series/deps.edn: -------------------------------------------------------------------------------- 1 | {:paths 2 | ["src" "test"] 3 | } 4 | -------------------------------------------------------------------------------- /spiral-matrix/deps.edn: -------------------------------------------------------------------------------- 1 | {:paths 2 | ["src" "test"]} 3 | -------------------------------------------------------------------------------- /two-fer/deps.edn: -------------------------------------------------------------------------------- 1 | {:paths 2 | ["src" "test"] 3 | } 4 | -------------------------------------------------------------------------------- /bank-account/deps.edn: -------------------------------------------------------------------------------- 1 | {:paths ["src" "test"]} 2 | 3 | 4 | -------------------------------------------------------------------------------- /armstrong-numbers/deps.edn: -------------------------------------------------------------------------------- 1 | { 2 | :paths ["src" "test"] 3 | } 4 | -------------------------------------------------------------------------------- /etl/deps.edn: -------------------------------------------------------------------------------- 1 | {;; quick hack for cider test runner :) 2 | :paths ["src" "test"]} 3 | -------------------------------------------------------------------------------- /hamming/deps.edn: -------------------------------------------------------------------------------- 1 | {;; quick hack for cider test runner :) 2 | :paths ["src" "test"]} 3 | -------------------------------------------------------------------------------- /pangram/deps.edn: -------------------------------------------------------------------------------- 1 | {;; quick hack for cider test runner :) 2 | :paths ["src" "test"]} 3 | -------------------------------------------------------------------------------- /pov/deps.edn: -------------------------------------------------------------------------------- 1 | {;; quick hack for cider test runner :) 2 | :paths ["src" "test"]} 3 | -------------------------------------------------------------------------------- /gigasecond/deps.edn: -------------------------------------------------------------------------------- 1 | {;; quick hack for cider test runner :) 2 | :paths ["src" "test"]} 3 | -------------------------------------------------------------------------------- /phone-number/deps.edn: -------------------------------------------------------------------------------- 1 | {;; quick hack for cider test runner :) 2 | :paths ["src" "test"]} 3 | -------------------------------------------------------------------------------- /word-count/deps.edn: -------------------------------------------------------------------------------- 1 | {;; quick hack for cider test runner :) 2 | :paths ["src" "test"]} 3 | -------------------------------------------------------------------------------- /isbn-verifier/deps.edn: -------------------------------------------------------------------------------- 1 | {;; quick hack for cider test runner :) 2 | :paths ["src" "test"]} 3 | -------------------------------------------------------------------------------- /rna-transcription/deps.edn: -------------------------------------------------------------------------------- 1 | {;; quick hack for cider test runner :) 2 | :paths ["src" "test"]} 3 | -------------------------------------------------------------------------------- /bob/deps.edn: -------------------------------------------------------------------------------- 1 | { 2 | ;; quick hack for cider test runner :) 3 | :paths ["src" "test"] 4 | 5 | } 6 | -------------------------------------------------------------------------------- /collatz-conjecture/deps.edn: -------------------------------------------------------------------------------- 1 | {;; quick hack for cider test runner :) 2 | :paths ["src" "test"]} 3 | -------------------------------------------------------------------------------- /run-length-encoding/deps.edn: -------------------------------------------------------------------------------- 1 | {;; quick hack for cider test runner :) 2 | :paths ["src" "test"]} 3 | -------------------------------------------------------------------------------- /etl/src/etl.clj: -------------------------------------------------------------------------------- 1 | (ns etl) 2 | 3 | (defn transform [source] ;; <- arglist goes here 4 | ;; your code goes here 5 | ) 6 | -------------------------------------------------------------------------------- /hello-world/src/hello_world.clj: -------------------------------------------------------------------------------- 1 | (ns hello-world) 2 | 3 | (defn hello [] ;; <- arglist goes here 4 | ;; your code goes here 5 | "Hello, World!" 6 | ) 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | /classes 3 | /checkouts 4 | *.jar 5 | *.class 6 | /.cpcache 7 | /.lein-* 8 | /.nrepl-history 9 | **/.nrepl-port 10 | .hgignore 11 | .hg/ 12 | .exercism/ 13 | -------------------------------------------------------------------------------- /.dir-locals.el: -------------------------------------------------------------------------------- 1 | ((clojure-mode . ((projectile-project-type . clojure-cli) 2 | (cider-preferred-build-tool . clojure-cli) 3 | (cider-clojure-cli-aliases . ":env/test")))) 4 | -------------------------------------------------------------------------------- /pov/src/pov.clj: -------------------------------------------------------------------------------- 1 | (ns pov) 2 | 3 | (defn of [] ;; <- arglist goes here 4 | ;; your code goes here 5 | ) 6 | 7 | (defn path-from-to [] ;; <- arglist goes here 8 | ;; your code goes here 9 | ) 10 | -------------------------------------------------------------------------------- /bob/project.clj: -------------------------------------------------------------------------------- 1 | (defproject bob "0.1.0-SNAPSHOT" 2 | :description "bob exercise." 3 | :url "https://github.com/exercism/clojure/tree/master/exercises/bob" 4 | :dependencies [[org.clojure/clojure "1.10.0"]]) 5 | -------------------------------------------------------------------------------- /two-fer/src/two_fer.clj: -------------------------------------------------------------------------------- 1 | (ns two-fer) 2 | 3 | (defn two-fer 4 | ([] (two-fer "you")) 5 | ([name] 6 | (str "One for " name ", one for me."))) 7 | 8 | (comment 9 | (two-fer)) ;; (two-fer "Zaphod") 10 | -------------------------------------------------------------------------------- /word-count/src/word_count.clj: -------------------------------------------------------------------------------- 1 | (ns word-count 2 | (:require [clojure.string])) 3 | 4 | (defn word-count [phrase] 5 | (frequencies 6 | (map clojure.string/lower-case 7 | (re-seq #"\w+" phrase)))) 8 | -------------------------------------------------------------------------------- /clock/project.clj: -------------------------------------------------------------------------------- 1 | (defproject clock "0.1.0-SNAPSHOT" 2 | :description "clock exercise." 3 | :url "https://github.com/exercism/clojure/tree/master/exercises/clock" 4 | :dependencies [[org.clojure/clojure "1.10.0"]]) 5 | -------------------------------------------------------------------------------- /anagram/project.clj: -------------------------------------------------------------------------------- 1 | (defproject anagram "0.1.0-SNAPSHOT" 2 | :description "anagram exercise." 3 | :url "https://github.com/exercism/clojure/tree/master/exercises/anagram" 4 | :dependencies [[org.clojure/clojure "1.10.0"]]) 5 | -------------------------------------------------------------------------------- /clock/README.md: -------------------------------------------------------------------------------- 1 | # Clock 2 | 3 | Implement a clock that handles times without dates. 4 | 5 | You should be able to add and subtract minutes to it. 6 | 7 | Two clocks that represent the same time should be equal to each other. 8 | -------------------------------------------------------------------------------- /hello-world/test/hello_world_test.clj: -------------------------------------------------------------------------------- 1 | (ns hello-world-test 2 | (:require [clojure.test :refer [deftest is]] 3 | hello-world)) 4 | 5 | (deftest hello-world-test 6 | (is (= "Hello, World!" (hello-world/hello)))) 7 | -------------------------------------------------------------------------------- /two-fer/project.clj: -------------------------------------------------------------------------------- 1 | (defproject two-fer "0.1.0-SNAPSHOT" 2 | :description "two-fer exercise." 3 | :url "https://github.com/exercism/clojure/tree/master/exercises/two-fer" 4 | :dependencies [[org.clojure/clojure "1.8.0"]]) 5 | -------------------------------------------------------------------------------- /beer-song/project.clj: -------------------------------------------------------------------------------- 1 | (defproject beer-song "0.1.0-SNAPSHOT" 2 | :description "beer-song exercise." 3 | :url "https://github.com/exercism/clojure/tree/master/exercises/beer-song" 4 | :dependencies [[org.clojure/clojure "1.10.0"]]) 5 | -------------------------------------------------------------------------------- /space-age/project.clj: -------------------------------------------------------------------------------- 1 | (defproject space-age "0.1.0-SNAPSHOT" 2 | :description "space-age exercise." 3 | :url "https://github.com/exercism/clojure/tree/master/exercises/space-age" 4 | :dependencies [[org.clojure/clojure "1.10.0"]]) 5 | -------------------------------------------------------------------------------- /hello-world/project.clj: -------------------------------------------------------------------------------- 1 | (defproject hello-world "0.1.0-SNAPSHOT" 2 | :description "hello-world exercise." 3 | :url "https://github.com/exercism/clojure/tree/master/exercises/hello-world" 4 | :dependencies [[org.clojure/clojure "1.10.0"]]) 5 | -------------------------------------------------------------------------------- /spiral-matrix/project.clj: -------------------------------------------------------------------------------- 1 | (defproject spiral-matrix "0.1.0-SNAPSHOT" 2 | :description "spiral-matrix exercise" 3 | :url "https://github.com/exercism/clojure/tree/master/exercises/spiral-matrix" 4 | :dependencies [[org.clojure/clojure "1.10.0"]]) 5 | -------------------------------------------------------------------------------- /reverse-string/project.clj: -------------------------------------------------------------------------------- 1 | (defproject reverse-string "0.1.0-SNAPSHOT" 2 | :description "reverse-string exercise." 3 | :url "https://github.com/exercism/clojure/tree/master/exercises/reverse-string" 4 | :dependencies [[org.clojure/clojure "1.10.0"]]) 5 | -------------------------------------------------------------------------------- /nucleotide-count/project.clj: -------------------------------------------------------------------------------- 1 | (defproject nucleotide-count "0.1.0-SNAPSHOT" 2 | :description "nucleotide-count exercise." 3 | :url "https://github.com/exercism/clojure/tree/master/exercises/nucleotide-count" 4 | :dependencies [[org.clojure/clojure "1.10.0"]]) 5 | -------------------------------------------------------------------------------- /armstrong-numbers/project.clj: -------------------------------------------------------------------------------- 1 | (defproject armstrong-numbers "0.1.0-SNAPSHOT" 2 | :description "armstrong-numbers exercise" 3 | :url "https://github.com/exercism/clojure/tree/master/exercises/armstrong-numbers" 4 | :dependencies [[org.clojure/clojure "1.10.0"]]) 5 | -------------------------------------------------------------------------------- /run-length-encoding/src/run_length_encoding.clj: -------------------------------------------------------------------------------- 1 | (ns run-length-encoding) 2 | 3 | (defn run-length-encode 4 | "encodes a string with run-length-encoding" 5 | [plain-text]) 6 | 7 | (defn run-length-decode 8 | "decodes a run-length-encoded string" 9 | [cipher-text]) 10 | -------------------------------------------------------------------------------- /series/src/series.clj: -------------------------------------------------------------------------------- 1 | (ns series) 2 | 3 | #_(defn slices 4 | [string length] 5 | (distinct 6 | (map #(apply str %) 7 | (partition length 1 string)))) 8 | 9 | (defn slices [string length] 10 | (distinct 11 | (map (partial apply ,,, str) 12 | (partition length 1 string)))) 13 | -------------------------------------------------------------------------------- /two-fer/mentor-notes.md: -------------------------------------------------------------------------------- 1 | Thank you for submitting this solution. 2 | 3 | One slight refinement you could submit as a further solution would be for the single argument branch to call the one argument branch. 4 | 5 | In more involved examples this can save code duplication, especially if the branches only differ slightly. 6 | -------------------------------------------------------------------------------- /anagram/src/anagram.clj: -------------------------------------------------------------------------------- 1 | (ns anagram) 2 | 3 | 4 | (defn word->character-set [word] 5 | (into set (map str (seq word)))) 6 | 7 | (defn anagrams-for [word prospect-list] 8 | (let [word-character-set (word->character-set word) 9 | prospect-list-character-sets (map word->character-set prospect-list)] 10 | (filter word-character-set prospect-list-character-sets))) 11 | -------------------------------------------------------------------------------- /two-fer/test/two_fer_test.clj: -------------------------------------------------------------------------------- 1 | (ns two-fer-test 2 | (:require [clojure.test :refer [deftest is]] 3 | two-fer)) 4 | 5 | (deftest two-fer-test 6 | (is (= "One for you, one for me." (two-fer/two-fer)))) 7 | 8 | (deftest name-alice-test 9 | (is (= "One for Alice, one for me." (two-fer/two-fer "Alice")))) 10 | 11 | (deftest name-bob-test 12 | (is (= "One for Bob, one for me." (two-fer/two-fer "Bob")))) 13 | -------------------------------------------------------------------------------- /pangram/src/pangram.clj: -------------------------------------------------------------------------------- 1 | (ns pangram 2 | (:require [clojure.string :as str])) 3 | 4 | (defn- valid? [sentence] 5 | (->> sentence 6 | (re-seq #"\p{IsAlphabetic}") 7 | (apply str) 8 | (str/lower-case) 9 | (sort) 10 | (distinct))) 11 | 12 | (defn pangram? [sentence] 13 | (let [alphabet (map char (range (int \a) (inc (int \z))))] 14 | (cond 15 | (= (valid? sentence) alphabet) true 16 | :else false))) 17 | -------------------------------------------------------------------------------- /gigasecond/README.md: -------------------------------------------------------------------------------- 1 | # Gigasecond 2 | 3 | Given a moment, determine the moment that would be after a gigasecond 4 | has passed. 5 | 6 | A gigasecond is 10^9 (1,000,000,000) seconds. 7 | ## Source 8 | 9 | Chapter 9 in Chris Pine's online Learn to Program tutorial. [http://pine.fm/LearnToProgram/?Chapter=09](http://pine.fm/LearnToProgram/?Chapter=09) 10 | 11 | ## Submitting Incomplete Solutions 12 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 13 | -------------------------------------------------------------------------------- /word-count/README.md: -------------------------------------------------------------------------------- 1 | # Word Count 2 | 3 | Given a phrase, count the occurrences of each word in that phrase. 4 | 5 | For example for the input `"olly olly in come free"` 6 | 7 | ```text 8 | olly: 2 9 | in: 1 10 | come: 1 11 | free: 1 12 | ``` 13 | ## Source 14 | 15 | This is a classic toy problem, but we were reminded of it by seeing it in the Go Tour. 16 | 17 | ## Submitting Incomplete Solutions 18 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 19 | -------------------------------------------------------------------------------- /pangram/README.md: -------------------------------------------------------------------------------- 1 | # Pangram 2 | 3 | Determine if a sentence is a pangram. A pangram (Greek: παν γράμμα, pan gramma, 4 | "every letter") is a sentence using every letter of the alphabet at least once. 5 | 6 | The best known English pangram is: 7 | 8 | _The quick brown fox jumps over the lazy dog_ 9 | 10 | The alphabet used consists of ASCII letters `a` to `z`, inclusive, and is case 11 | insensitive. Input will not contain non-ASCII symbols. 12 | 13 | ## Source 14 | 15 | Wikipedia [https://en.wikipedia.org/wiki/Pangram](https://en.wikipedia.org/wiki/Pangram) 16 | -------------------------------------------------------------------------------- /collatz-conjecture/src/collatz_conjecture.clj: -------------------------------------------------------------------------------- 1 | (ns collatz-conjecture) 2 | 3 | (defn collatz-step [num] 4 | (if (even? num) 5 | (/ num 2) 6 | (inc (* num 3)))) 7 | 8 | (defn collatz 9 | "Given a starting num, returns the count of steps needed for the Collatz Conjecture to reach 1" 10 | ([num] 11 | (assert (and (number? num) 12 | (> num 0)) 13 | "num should be a positive number") 14 | (->> num 15 | (iterate collatz-step) 16 | (take-while #(not= % 1)) 17 | count))) 18 | 19 | (comment 20 | (collatz 20)) ;; 7 21 | -------------------------------------------------------------------------------- /clock/src/clock.clj: -------------------------------------------------------------------------------- 1 | (ns clock) 2 | 3 | (defn clock->string [clock] 4 | (let [h (:hours clock) 5 | m (:minutes clock)] 6 | (format "%02d:%02d" h m))) 7 | 8 | (defn minutes [clock] 9 | (+ (:minutes clock) (* (:hours clock) 60))) 10 | 11 | (defn adjusted-minutes [n] 12 | (mod (+ n 1440) 1440)) 13 | 14 | (defn add-time [clock time] 15 | (let [m (adjusted-minutes (+ time (minutes clock)))] 16 | {:hours (quot m 60) :minutes (mod m 60)})) 17 | 18 | (defn clock 19 | ([] {:hours 0 :minutes 0}) 20 | ([hours minutes] (add-time (clock) (+ minutes (* hours 60))))) 21 | -------------------------------------------------------------------------------- /gigasecond/test/gigasecond_test.clj: -------------------------------------------------------------------------------- 1 | (ns gigasecond-test 2 | (:require [clojure.test :refer [deftest is]] 3 | gigasecond)) 4 | 5 | (deftest from-apr-25-2011 6 | (is (= [2043 1 1] (gigasecond/from 2011 4 25)))) 7 | 8 | (deftest from-jun-13-1977 9 | (is (= [2009 2 19] (gigasecond/from 1977 6 13)))) 10 | 11 | (deftest from-jul-19-1959 12 | (is (= [1991 3 27] (gigasecond/from 1959 7 19)))) 13 | 14 | ;; customize this to test your birthday and find your gigasecond date: 15 | ;; (deftest your-birthday 16 | ;; (is (= [year2 month2 day2] (gigasecond/from year1 month1 day1)))) 17 | -------------------------------------------------------------------------------- /isbn-verifier/src/isbn_verifier.clj: -------------------------------------------------------------------------------- 1 | (ns isbn-verifier) 2 | 3 | (defn- normalized-isbn [isbn] 4 | (->> isbn 5 | (re-find #"(\d)-?(\d{3})-?(\d{5})-?([\dX])$") 6 | next 7 | (apply str))) 8 | 9 | (defn- parse-isbn-char [x] 10 | (if (= x \X) 11 | 10 12 | (Character/digit x 10))) 13 | 14 | (defn- isbn-sum [s] 15 | (->> s 16 | reverse 17 | (map-indexed (fn [idx itm] (* (inc idx) (parse-isbn-char itm)))) 18 | (apply +))) 19 | 20 | (defn isbn? [isbn] 21 | (let [s (normalized-isbn isbn)] 22 | (if (empty? s) 23 | false 24 | (zero? (mod (isbn-sum s) 11))))) 25 | -------------------------------------------------------------------------------- /gigasecond/src/gigasecond.clj: -------------------------------------------------------------------------------- 1 | ;; --------------------------------------------------------- 2 | ;; Exercisim: gigasecond 3 | ;; 4 | ;; See Design Journal namespace for many different examples 5 | ;; --------------------------------------------------------- 6 | 7 | 8 | (ns gigasecond 9 | (:import [java.time LocalDate])) 10 | 11 | (def days-of-gigasecond (long (/ 1000000000 86400))) 12 | 13 | (defn from [year month day] 14 | (let [giga-day (.plusDays (LocalDate/of year month day) 15 | days-of-gigasecond)] 16 | [(.getYear giga-day) 17 | (.getMonthValue giga-day) 18 | (.getDayOfMonth giga-day)])) 19 | -------------------------------------------------------------------------------- /reverse-string/README.md: -------------------------------------------------------------------------------- 1 | # Reverse String 2 | 3 | Reverse a string 4 | 5 | For example: 6 | input: "cool" 7 | output: "looc" 8 | 9 | ## Restrictions 10 | You can not use the `reverse` function, but you can use any of the other core functions. 11 | 12 | ## Source 13 | 14 | Introductory challenge to reverse an input string [https://medium.freecodecamp.org/how-to-reverse-a-string-in-javascript-in-3-different-ways-75e4763c68cb](https://medium.freecodecamp.org/how-to-reverse-a-string-in-javascript-in-3-different-ways-75e4763c68cb) 15 | 16 | ## Submitting Incomplete Solutions 17 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 18 | -------------------------------------------------------------------------------- /series/test/series_test.clj: -------------------------------------------------------------------------------- 1 | (ns series-test 2 | (:require [clojure.test :refer [deftest is testing]] 3 | [series :refer [slices]])) 4 | 5 | (deftest empty-string 6 | (testing "empty string with any number" 7 | (is (= [] (slices "" 1))))) 8 | 9 | (deftest number-eq-zero 10 | (testing "number = 0" 11 | (is (= [""] (slices "123" 0))))) 12 | 13 | (deftest number>string 14 | (testing "number > string-length" 15 | (is (= [] (slices "123" 1000))))) 16 | 17 | (deftest number=string 18 | (testing "number = string-length" 19 | (is (= ["123"] (slices "123" 3))))) 20 | 21 | (deftest number> sentence 14 | (re-seq #"\p{IsAlphabetic}") 15 | (apply str) 16 | (str/lower-case) 17 | (sort) 18 | (distinct))) 19 | 20 | (defn pangram? [sentence] 21 | (let [alphabet (map char (range (int \a) (inc (int \z))))] 22 | (cond 23 | (= (valid? sentence) alphabet) true 24 | :else false)))) 25 | -------------------------------------------------------------------------------- /isbn-verifier/src/design_journal.clj: -------------------------------------------------------------------------------- 1 | (ns design-journal) 2 | 3 | ;; Rich comment block with redefined vars ignored 4 | (comment 5 | 6 | (defn- normalized-isbn [isbn] 7 | (->> isbn 8 | (re-find #"(\d)-?(\d{3})-?(\d{5})-?([\dX])$") 9 | next 10 | (apply str))) 11 | 12 | (defn- parse-isbn-char [x] 13 | (if (= x \X) 14 | 10 15 | (Character/digit x 10))) 16 | 17 | (defn- isbn-sum [s] 18 | (->> s 19 | reverse 20 | (map-indexed (fn [idx itm] (* (inc idx) (parse-isbn-char itm)))) 21 | (apply +))) 22 | 23 | (defn isbn? [isbn] 24 | (let [s (normalized-isbn isbn)] 25 | (if (empty? s) 26 | false 27 | (zero? (mod (isbn-sum s) 11)))))) 28 | 29 | ;; End of rich comment block 30 | -------------------------------------------------------------------------------- /space-age/src/space_age.clj: -------------------------------------------------------------------------------- 1 | (ns space-age) 2 | 3 | (defn seconds-to-earth-years 4 | "Convert sections to earth years" 5 | [seconds] 6 | (/ seconds 31557600.0)) 7 | 8 | (defn relative-age 9 | "Return function to calculate age 10 | based on each planets relative orbit" 11 | [realtve-orbit] 12 | (fn [seconds] 13 | (let [earth-years (seconds-to-earth-years seconds)] 14 | (/ earth-years realtve-orbit)))) 15 | 16 | 17 | (def on-mercury (relative-age 0.240846)) 18 | (def on-venus (relative-age 0.6151972)) 19 | (def on-earth (relative-age 1)) 20 | (def on-mars (relative-age 1.8808158)) 21 | (def on-jupiter (relative-age 11.862615)) 22 | (def on-saturn (relative-age 29.447498)) 23 | (def on-neptune (relative-age 164.79132)) 24 | (def on-uranus (relative-age 84.016846)) 25 | -------------------------------------------------------------------------------- /rna-transcription/test/rna_transcription_test.clj: -------------------------------------------------------------------------------- 1 | (ns rna-transcription-test 2 | (:require [clojure.test :refer [deftest is]] 3 | rna-transcription)) 4 | 5 | (deftest transcribes-cytosine-to-guanine 6 | (is (= "G" (rna-transcription/to-rna "C")))) 7 | 8 | (deftest transcribes-guanine-to-cytosine 9 | (is (= "C" (rna-transcription/to-rna "G")))) 10 | 11 | (deftest transcribes-adenine-to-uracil 12 | (is (= "U" (rna-transcription/to-rna "A")))) 13 | 14 | (deftest it-transcribes-thymine-to-adenine 15 | (is (= "A" (rna-transcription/to-rna "T")))) 16 | 17 | (deftest it-transcribes-all-nucleotides 18 | (is (= "UGCACCAGAAUU" (rna-transcription/to-rna "ACGTGGTCTTAA")))) 19 | 20 | (deftest it-validates-dna-strands 21 | (is (thrown? AssertionError (rna-transcription/to-rna "XCGFGGTDTTAA")))) 22 | -------------------------------------------------------------------------------- /two-fer/README.md: -------------------------------------------------------------------------------- 1 | # Two Fer 2 | 3 | `Two-fer` or `2-fer` is short for two for one. One for you and one for me. 4 | 5 | Given a name, return a string with the message: 6 | 7 | ```text 8 | One for X, one for me. 9 | ``` 10 | 11 | Where X is the given name. 12 | 13 | However, if the name is missing, return the string: 14 | 15 | ```text 16 | One for you, one for me. 17 | ``` 18 | 19 | Here are some examples: 20 | 21 | |Name | String to return 22 | |:------:|:-----------------: 23 | |Alice | One for Alice, one for me. 24 | |Bob | One for Bob, one for me. 25 | | | One for you, one for me. 26 | |Zaphod | One for Zaphod, one for me. 27 | 28 | 29 | ## Source 30 | 31 | [https://github.com/exercism/problem-specifications/issues/757](https://github.com/exercism/problem-specifications/issues/757) 32 | -------------------------------------------------------------------------------- /hamming/test/hamming_test.clj: -------------------------------------------------------------------------------- 1 | (ns hamming-test 2 | (:require [clojure.test :refer [deftest is]] 3 | hamming)) 4 | 5 | (deftest no-difference-between-empty-strands 6 | (is (= 0 (hamming/distance "" "")))) 7 | 8 | (deftest no-difference-between-identical-strands 9 | (is (= 0 (hamming/distance "GGACTGA" "GGACTGA")))) 10 | 11 | (deftest complete-distance-in-small-strand 12 | (is (= 3 (hamming/distance "ACT" "GGA")))) 13 | 14 | (deftest small-distance-in-middle-somewhere 15 | (is (= 1 (hamming/distance "GGACG" "GGTCG")))) 16 | 17 | (deftest larger-distance 18 | (is (= 2 (hamming/distance "ACCAGGG" "ACTATGG")))) 19 | 20 | (deftest undefined-when-lengths-are-different 21 | (is (= nil (hamming/distance "AAAC" "TAGGGGAGGCTAGCGGTAGGAC"))) 22 | (is (= nil (hamming/distance "GACTACGGACAGGGTAACATAG" "GACA")))) 23 | -------------------------------------------------------------------------------- /collatz-conjecture/test/collatz_conjecture_test.clj: -------------------------------------------------------------------------------- 1 | (ns collatz-conjecture-test 2 | (:require [clojure.test :refer [deftest is testing]] 3 | [collatz-conjecture :refer [collatz]])) 4 | 5 | (deftest steps-for-1 6 | (testing "zero steps for one" 7 | (is (= 0 (collatz 1))))) 8 | 9 | (deftest steps-for-16 10 | (testing "divide if even" 11 | (is (= 4 (collatz 16))))) 12 | 13 | (deftest steps-for-12 14 | (testing "even and odd steps" 15 | (is (= 9 (collatz 12))))) 16 | 17 | (deftest steps-for-1000000 18 | (testing "Large number of even and odd steps" 19 | (is (= 152 (collatz 1000000))))) 20 | 21 | (deftest steps-for-0 22 | (testing "zero is an error" 23 | (is (thrown? Throwable 24 | (collatz 0))))) 25 | 26 | (deftest steps-for-negative 27 | (testing "negative value is an error" 28 | (is (thrown? Throwable 29 | (collatz -15))))) 30 | -------------------------------------------------------------------------------- /armstrong-numbers/README.md: -------------------------------------------------------------------------------- 1 | # Armstrong Numbers 2 | 3 | An [Armstrong number](https://en.wikipedia.org/wiki/Narcissistic_number) is a number that is the sum of its own digits each raised to the power of the number of digits. 4 | 5 | For example: 6 | 7 | - 9 is an Armstrong number, because `9 = 9^1 = 9` 8 | - 10 is *not* an Armstrong number, because `10 != 1^2 + 0^2 = 1` 9 | - 153 is an Armstrong number, because: `153 = 1^3 + 5^3 + 3^3 = 1 + 125 + 27 = 153` 10 | - 154 is *not* an Armstrong number, because: `154 != 1^3 + 5^3 + 4^3 = 1 + 125 + 64 = 190` 11 | 12 | Write some code to determine whether a number is an Armstrong number. 13 | ## Source 14 | 15 | Wikipedia [https://en.wikipedia.org/wiki/Narcissistic_number](https://en.wikipedia.org/wiki/Narcissistic_number) 16 | 17 | ## Submitting Incomplete Solutions 18 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 19 | -------------------------------------------------------------------------------- /reverse-string/src/reverse_string.clj: -------------------------------------------------------------------------------- 1 | (ns reverse-string 2 | (:require clojure.string)) 3 | 4 | 5 | (defn reverse-string [s] 6 | (apply str (into '() s))) 7 | 8 | 9 | ;; Rich comment block with redefined vars ignored 10 | #_{:clj-kondo/ignore [:redefined-var]} 11 | (comment 12 | 13 | 14 | (defn reverse-string [s] 15 | (clojure.string/reverse s)) 16 | 17 | 18 | (defn reverse-string [s] 19 | (apply str (reduce conj '() s))) 20 | 21 | 22 | (defn reverse-string [s] 23 | (let [f (fn [x y] (cons y x))] 24 | (apply str (reduce f "" s))) 25 | ) 26 | 27 | (reverse-string "hello") 28 | 29 | ) ;; End of rich comment block 30 | 31 | 32 | 33 | ;; Rich comment block with redefined vars ignored 34 | #_{:clj-kondo/ignore [:redefined-var]} 35 | (comment 36 | 37 | ;; removes the need for clojure.string/join 38 | ;; or apply str 39 | 40 | (reduce #(str %2 %1) "" "hello") 41 | 42 | 43 | ) ;; End of rich comment block 44 | -------------------------------------------------------------------------------- /bob/README.md: -------------------------------------------------------------------------------- 1 | # Bob 2 | 3 | Bob is a lackadaisical teenager. In conversation, his responses are very limited. 4 | 5 | Bob answers 'Sure.' if you ask him a question. 6 | 7 | He answers 'Whoa, chill out!' if you yell at him. 8 | 9 | He answers 'Calm down, I know what I'm doing!' if you yell a question at him. 10 | 11 | He says 'Fine. Be that way!' if you address him without actually saying 12 | anything. 13 | 14 | He answers 'Whatever.' to anything else. 15 | 16 | Bob's conversational partner is a purist when it comes to written communication and always follows normal rules regarding sentence punctuation in English. 17 | ## Source 18 | 19 | Inspired by the 'Deaf Grandma' exercise in Chris Pine's Learn to Program tutorial. [http://pine.fm/LearnToProgram/?Chapter=06](http://pine.fm/LearnToProgram/?Chapter=06) 20 | 21 | ## Submitting Incomplete Solutions 22 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 23 | -------------------------------------------------------------------------------- /rna-transcription/README.md: -------------------------------------------------------------------------------- 1 | # RNA Transcription 2 | 3 | Given a DNA strand, return its RNA complement (per RNA transcription). 4 | 5 | Both DNA and RNA strands are a sequence of nucleotides. 6 | 7 | The four nucleotides found in DNA are adenine (**A**), cytosine (**C**), 8 | guanine (**G**) and thymine (**T**). 9 | 10 | The four nucleotides found in RNA are adenine (**A**), cytosine (**C**), 11 | guanine (**G**) and uracil (**U**). 12 | 13 | Given a DNA strand, its transcribed RNA strand is formed by replacing 14 | each nucleotide with its complement: 15 | 16 | * `G` -> `C` 17 | * `C` -> `G` 18 | * `T` -> `A` 19 | * `A` -> `U` 20 | ## Source 21 | 22 | Hyperphysics [http://hyperphysics.phy-astr.gsu.edu/hbase/Organic/transcription.html](http://hyperphysics.phy-astr.gsu.edu/hbase/Organic/transcription.html) 23 | 24 | ## Submitting Incomplete Solutions 25 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 26 | -------------------------------------------------------------------------------- /anagram/README.md: -------------------------------------------------------------------------------- 1 | # Anagram 2 | 3 | Welcome to Anagram on Exercism's Clojure Track. 4 | If you need help running the tests or submitting your code, check out `HELP.md`. 5 | 6 | ## Instructions 7 | 8 | An anagram is a rearrangement of letters to form a new word. 9 | Given a word and a list of candidates, select the sublist of anagrams of the given word. 10 | 11 | Given `"listen"` and a list of candidates like `"enlists" "google" 12 | "inlets" "banana"` the program should return a list containing 13 | `"inlets"`. 14 | 15 | ## Source 16 | 17 | ### Created by 18 | 19 | - @rubysolo 20 | 21 | ### Contributed to by 22 | 23 | - @achengs 24 | - @AndreaCrotti 25 | - @canweriotnow 26 | - @crazymykl 27 | - @dkinzer 28 | - @haus 29 | - @henrik 30 | - @jgwhite 31 | - @kytrinyx 32 | - @markijbema 33 | - @mascip 34 | - @mathias 35 | - @tejasbubane 36 | - @yurrriq 37 | 38 | ### Based on 39 | 40 | Inspired by the Extreme Startup game - https://github.com/rchatley/extreme_startup -------------------------------------------------------------------------------- /rna-transcription/src/rna-transcription-pure.clj: -------------------------------------------------------------------------------- 1 | (ns rna-transcription-pure 2 | (:require [clojure.string :as string])) 3 | 4 | (defn dna->rna 5 | "Transcribe each nucleotide from a DNA strand into its RNA complement 6 | Arguments: string representing DNA strand 7 | Return: string representing RNA strand" 8 | [transcription dna] 9 | (string/join 10 | (map #(or (transcription %) 11 | (throw (AssertionError. "Unknown nucleotide"))) 12 | dna ))) 13 | 14 | (map) 15 | 16 | (comment 17 | 18 | (require '[rna-transcription :refer [dna-nucleotide->rna-nucleotide]]) 19 | 20 | (dna->rna dna-nucleotide->rna-nucleotide "GATTAGA") 21 | 22 | (dna->rna dna-nucleotide->rna-nucleotide "hello") 23 | ) 24 | 25 | (def dna-complement {\G \C, \C \G, \T \A, \A \U}) 26 | 27 | 28 | (defn to-rna [dna] 29 | {:pre [(every? dna-complement dna)]} 30 | (->> dna 31 | (map dna-complement) 32 | (apply str))) 33 | 34 | (to-rna "X") 35 | -------------------------------------------------------------------------------- /spiral-matrix/README.md: -------------------------------------------------------------------------------- 1 | # Spiral Matrix 2 | 3 | Given the size, return a square matrix of numbers in spiral order. 4 | 5 | The matrix should be filled with natural numbers, starting from 1 6 | in the top-left corner, increasing in an inward, clockwise spiral order, 7 | like these examples: 8 | 9 | ###### Spiral matrix of size 3 10 | 11 | ```text 12 | 1 2 3 13 | 8 9 4 14 | 7 6 5 15 | ``` 16 | 17 | ###### Spiral matrix of size 4 18 | 19 | ```text 20 | 1 2 3 4 21 | 12 13 14 5 22 | 11 16 15 6 23 | 10 9 8 7 24 | ``` 25 | ## Source 26 | 27 | Reddit r/dailyprogrammer challenge #320 [Easy] Spiral Ascension. [https://www.reddit.com/r/dailyprogrammer/comments/6i60lr/20170619_challenge_320_easy_spiral_ascension/](https://www.reddit.com/r/dailyprogrammer/comments/6i60lr/20170619_challenge_320_easy_spiral_ascension/) 28 | 29 | ## Submitting Incomplete Solutions 30 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 31 | -------------------------------------------------------------------------------- /nucleotide-count/README.md: -------------------------------------------------------------------------------- 1 | # Nucleotide Count 2 | 3 | Given a single stranded DNA string, compute how many times each nucleotide occurs in the string. 4 | 5 | The genetic language of every living thing on the planet is DNA. 6 | DNA is a large molecule that is built from an extremely long sequence of individual elements called nucleotides. 7 | 4 types exist in DNA and these differ only slightly and can be represented as the following symbols: 'A' for adenine, 'C' for cytosine, 'G' for guanine, and 'T' thymine. 8 | 9 | Here is an analogy: 10 | - twigs are to birds nests as 11 | - nucleotides are to DNA as 12 | - legos are to lego houses as 13 | - words are to sentences as... 14 | ## Source 15 | 16 | The Calculating DNA Nucleotides_problem at Rosalind [http://rosalind.info/problems/dna/](http://rosalind.info/problems/dna/) 17 | 18 | ## Submitting Incomplete Solutions 19 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 20 | -------------------------------------------------------------------------------- /word-count/test/word_count_test.clj: -------------------------------------------------------------------------------- 1 | (ns word-count-test 2 | (:require [clojure.test :refer [deftest is]] 3 | word-count)) 4 | 5 | (deftest count-one-word 6 | (is (= {"word" 1} 7 | (word-count/word-count "word")))) 8 | 9 | (deftest count-one-of-each 10 | (is (= {"one" 1 "of" 1 "each" 1} 11 | (word-count/word-count "one of each")))) 12 | 13 | (deftest count-multiple-occurrences 14 | (is (= {"one" 1 "fish" 4 "two" 1 "red" 1 "blue" 1} 15 | (word-count/word-count "one fish two fish red fish blue fish")))) 16 | 17 | (deftest ignore-punctuation 18 | (is (= {"car" 1, "carpet" 1 "as" 1 "java" 1 "javascript" 1} 19 | (word-count/word-count "car : carpet as java : javascript!!&@$%^&")))) 20 | 21 | (deftest include-numbers 22 | (is (= {"testing" 2 "1" 1 "2" 1} 23 | (word-count/word-count "testing, 1, 2 testing")))) 24 | 25 | (deftest normalize-case 26 | (is (= {"go" 3} 27 | (word-count/word-count "go Go GO")))) 28 | -------------------------------------------------------------------------------- /collatz-conjecture/README.md: -------------------------------------------------------------------------------- 1 | # Collatz Conjecture 2 | 3 | The Collatz Conjecture or 3x+1 problem can be summarized as follows: 4 | 5 | Take any positive integer n. 6 | 7 | If n is even, divide n by 2 to get n / 2. 8 | 9 | If n is odd, multiply n by 3 and add 1 to get 3n + 1. 10 | 11 | Repeat the process indefinitely. 12 | 13 | The conjecture states that no matter which number you start with, you will always reach 1 eventually. 14 | 15 | Given a number n, return the number of steps required to reach 1. 16 | 17 | 18 | ## Examples 19 | 20 | Starting with n = 12, the steps would be as follows: 21 | 22 | 0. 12 23 | 1. 6 24 | 2. 3 25 | 3. 10 26 | 4. 5 27 | 5. 16 28 | 6. 8 29 | 7. 4 30 | 8. 2 31 | 9. 1 32 | 33 | Resulting in 9 steps. So for input n = 12, the return value would be 9. 34 | 35 | 36 | ## Source 37 | 38 | An unsolved problem in mathematics named after mathematician Lothar Collatz [https://en.wikipedia.org/wiki/3x_%2B_1_problem](https://en.wikipedia.org/wiki/3x_%2B_1_problem) 39 | -------------------------------------------------------------------------------- /rna-transcription/test/rna_transcription_pure_test.clj: -------------------------------------------------------------------------------- 1 | (ns rna-transtription-pure-test 2 | (:require [clojure.test :refer [deftest is]] 3 | [rna-transcription-pure :as SUT] 4 | [rna-transcription :as data])) 5 | 6 | (deftest transcribes-cytosine-to-guanine 7 | (is (= "G" (SUT/dna->rna data/dna-nucleotide->rna-nucleotide "C")))) 8 | 9 | (deftest transcribes-guanine-to-cytosine 10 | (is (= "C" (SUT/dna->rna data/dna-nucleotide->rna-nucleotide "G")))) 11 | 12 | (deftest transcribes-adenine-to-uracil 13 | (is (= "U" (SUT/dna->rna data/dna-nucleotide->rna-nucleotide "A")))) 14 | 15 | (deftest it-transcribes-thymine-to-adenine 16 | (is (= "A" (SUT/dna->rna data/dna-nucleotide->rna-nucleotide "T")))) 17 | 18 | (deftest it-transcribes-all-nucleotides 19 | (is (= "UGCACCAGAAUU" (SUT/dna->rna data/dna-nucleotide->rna-nucleotide "ACGTGGTCTTAA")))) 20 | 21 | (deftest it-validates-dna-strands 22 | (is (thrown? AssertionError (SUT/dna->rna data/dna-nucleotide->rna-nucleotide "XCGFGGTDTTAA")))) 23 | -------------------------------------------------------------------------------- /phone-number/src/phone_number.clj: -------------------------------------------------------------------------------- 1 | (ns phone-number) 2 | 3 | ;; --------------------------------------------------------- 4 | (def phone-characters 5 | #{\. \( \) \@}) 6 | ;; --------------------------------------------------------- 7 | 8 | ;; --------------------------------------------------------- 9 | (defn clean-number [num-string phone-characters] 10 | (apply str (remove phone-characters num-string))) 11 | 12 | (defn number [num-string] 13 | 14 | ((cond 15 | (= 11 (count num-string)) (clean-number num-string phone-characters) 16 | :else nil))) 17 | 18 | (defn area-code [num-string]) ;; <- arglist goes here 19 | ;; your code goes here 20 | 21 | (defn pretty-print [num-string]) ;; <- arglist goes here 22 | ;; your code goes here 23 | 24 | ;; --------------------------------------------------------- 25 | 26 | ;; --------------------------------------------------------- 27 | ;; REPL experiements 28 | 29 | (comment 30 | ;; A set can be used as a function with remove 31 | (remove #{\@} "jenny@jetpack")) 32 | -------------------------------------------------------------------------------- /reverse-string/test/reverse_string_test.clj: -------------------------------------------------------------------------------- 1 | (ns reverse-string-test 2 | (:require [clojure.test :refer [deftest is]] 3 | reverse-string)) 4 | 5 | (deftest empty-string-test 6 | (is (= "" (reverse-string/reverse-string "")))) 7 | 8 | (deftest a-letter-test 9 | (is (= "I" (reverse-string/reverse-string "I")))) 10 | 11 | (deftest a-word-test 12 | (is (= "tobor" (reverse-string/reverse-string "robot")))) 13 | 14 | (deftest capitalised-word-test 15 | (is (= "nemaR" (reverse-string/reverse-string "Ramen")))) 16 | 17 | (deftest sentence-with-punctuation-test 18 | (is (= "!yrgnuh m'I" (reverse-string/reverse-string "I'm hungry!")))) 19 | 20 | (deftest palindrome-test 21 | (is (= "racecar" (reverse-string/reverse-string "racecar")))) 22 | 23 | (deftest even-sized-word-test 24 | (is (= "reward" (reverse-string/reverse-string "drawer")))) 25 | 26 | (deftest long-string-test 27 | (let [s (reduce str (repeat 1000 "overflow?")) 28 | rs (reduce str (repeat 1000 "?wolfrevo"))] 29 | (is (= rs (reverse-string/reverse-string s))))) 30 | -------------------------------------------------------------------------------- /run-length-encoding/README.md: -------------------------------------------------------------------------------- 1 | # Run Length Encoding 2 | 3 | Implement run-length encoding and decoding. 4 | 5 | Run-length encoding (RLE) is a simple form of data compression, where runs 6 | (consecutive data elements) are replaced by just one data value and count. 7 | 8 | For example we can represent the original 53 characters with only 13. 9 | 10 | ```text 11 | "WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB" -> "12WB12W3B24WB" 12 | ``` 13 | 14 | RLE allows the original data to be perfectly reconstructed from 15 | the compressed data, which makes it a lossless data compression. 16 | 17 | ```text 18 | "AABCCCDEEEE" -> "2AB3CD4E" -> "AABCCCDEEEE" 19 | ``` 20 | 21 | For simplicity, you can assume that the unencoded string will only contain 22 | the letters A through Z (either lower or upper case) and whitespace. This way 23 | data to be encoded will never contain any numbers and numbers inside data to 24 | be decoded always represent the count for the following character. 25 | ## Source 26 | 27 | Wikipedia [https://en.wikipedia.org/wiki/Run-length_encoding](https://en.wikipedia.org/wiki/Run-length_encoding) 28 | -------------------------------------------------------------------------------- /space-age/README.md: -------------------------------------------------------------------------------- 1 | # Space Age 2 | 3 | Given an age in seconds, calculate how old someone would be on: 4 | 5 | - Earth: orbital period 365.25 Earth days, or 31557600 seconds 6 | - Mercury: orbital period 0.2408467 Earth years 7 | - Venus: orbital period 0.61519726 Earth years 8 | - Mars: orbital period 1.8808158 Earth years 9 | - Jupiter: orbital period 11.862615 Earth years 10 | - Saturn: orbital period 29.447498 Earth years 11 | - Uranus: orbital period 84.016846 Earth years 12 | - Neptune: orbital period 164.79132 Earth years 13 | 14 | So if you were told someone were 1,000,000,000 seconds old, you should 15 | be able to say that they're 31.69 Earth-years old. 16 | 17 | If you're wondering why Pluto didn't make the cut, go watch [this 18 | youtube video](http://www.youtube.com/watch?v=Z_2gbGXzFbs). 19 | ## Source 20 | 21 | Partially inspired by Chapter 1 in Chris Pine's online Learn to Program tutorial. [http://pine.fm/LearnToProgram/?Chapter=01](http://pine.fm/LearnToProgram/?Chapter=01) 22 | 23 | ## Submitting Incomplete Solutions 24 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 25 | -------------------------------------------------------------------------------- /etl/test/etl_test.clj: -------------------------------------------------------------------------------- 1 | (ns etl-test 2 | (:require [clojure.test :refer [deftest is]] 3 | etl)) 4 | 5 | (deftest transform-one-value 6 | (is (= {"world" 1} 7 | (etl/transform {1 ["WORLD"]})))) 8 | 9 | (deftest transform-more-values 10 | (is (= {"world" 1 "gschoolers" 1} 11 | (etl/transform {1 ["WORLD" "GSCHOOLERS"]})))) 12 | 13 | (deftest more-keys 14 | (is (= {"apple" 1 "artichoke" 1 "boat" 2 "ballerina" 2} 15 | (etl/transform {1 ["APPLE" "ARTICHOKE"], 2 ["BOAT" "BALLERINA"]})))) 16 | 17 | (deftest full-dataset 18 | (is (= {"a" 1 "b" 3 "c" 3 "d" 2 "e" 1 19 | "f" 4 "g" 2 "h" 4 "i" 1 "j" 8 20 | "k" 5 "l" 1 "m" 3 "n" 1 "o" 1 21 | "p" 3 "q" 10 "r" 1 "s" 1 "t" 1 22 | "u" 1 "v" 4 "w" 4 "x" 8 "y" 4 23 | "z" 10} 24 | (etl/transform {1 (re-seq #"\w" "AEIOULNRST") 25 | 2 (re-seq #"\w" "DG") 26 | 3 (re-seq #"\w" "BCMP") 27 | 4 (re-seq #"\w" "FHVWY") 28 | 5 (re-seq #"\w" "K") 29 | 8 (re-seq #"\w" "JX") 30 | 10 (re-seq #"\w" "QZ")})))) 31 | -------------------------------------------------------------------------------- /bank-account/README.md: -------------------------------------------------------------------------------- 1 | # Bank Account 2 | 3 | Simulate a bank account supporting opening/closing, withdrawals, and deposits 4 | of money. Watch out for concurrent transactions! 5 | 6 | A bank account can be accessed in multiple ways. Clients can make 7 | deposits and withdrawals using the internet, mobile phones, etc. Shops 8 | can charge against the account. 9 | 10 | Create an account that can be accessed from multiple threads/processes 11 | (terminology depends on your programming language). 12 | 13 | It should be possible to close an account; operations against a closed 14 | account must fail. 15 | 16 | ## Instructions 17 | 18 | Run the test file, and fix each of the errors in turn. When you get the 19 | first test to pass, go to the first pending or skipped test, and make 20 | that pass as well. When all of the tests are passing, feel free to 21 | submit. 22 | 23 | Remember that passing code is just the first step. The goal is to work 24 | towards a solution that is as readable and expressive as you can make 25 | it. 26 | 27 | Have fun! 28 | 29 | ## Submitting Incomplete Solutions 30 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 31 | -------------------------------------------------------------------------------- /nucleotide-count/test/nucleotide_count_test.clj: -------------------------------------------------------------------------------- 1 | (ns nucleotide-count-test 2 | (:require [clojure.test :refer [deftest is]] 3 | nucleotide-count)) 4 | 5 | (deftest empty-dna-strand-has-no-adenosine 6 | (is (= 0 (nucleotide-count/count-of-nucleotide-in-strand \A, "")))) 7 | 8 | (deftest empty-dna-strand-has-no-nucleotides s 9 | (is (= {\A 0, \T 0, \C 0, \G 0} 10 | (nucleotide-count/nucleotide-counts "")))) 11 | 12 | (deftest repetitive-cytidine-gets-counted 13 | (is (= 5 (nucleotide-count/count-of-nucleotide-in-strand \C "CCCCC")))) 14 | 15 | (deftest repetitive-sequence-has-only-guanosine 16 | (is (= {\A 0, \T 0, \C 0, \G 8} 17 | (nucleotide-count/nucleotide-counts "GGGGGGGG")))) 18 | 19 | (deftest counts-only-thymidine 20 | (is (= 1 (nucleotide-count/count-of-nucleotide-in-strand \T "GGGGGTAACCCGG")))) 21 | 22 | (deftest validates-nucleotides 23 | (is (thrown? Throwable (nucleotide-count/count-of-nucleotide-in-strand \X "GACT")))) 24 | 25 | (deftest counts-all-nucleotides 26 | (let [s "AGCTTTTCATTCTGACTGCAACGGGCAATATGTCTCTGTGTGGATTAAAAAAAGAGTGTCTGATAGCAGC"] 27 | (is (= {\A 20, \T 21, \G 17, \C 12} 28 | (nucleotide-count/nucleotide-counts s))))) 29 | -------------------------------------------------------------------------------- /pangram/test/pangram_test.clj: -------------------------------------------------------------------------------- 1 | (ns pangram-test 2 | (:require [clojure.test :refer [is deftest]] 3 | [pangram :refer [pangram?]])) 4 | 5 | (deftest empty-sentence 6 | (is (false? (pangram? "")))) 7 | 8 | (deftest lowercase-pangram 9 | (is (pangram? "the quick brown fox jumps over the lazy dog"))) 10 | 11 | (deftest missing-character-x 12 | (is 13 | (false? 14 | (pangram? "a quick movement of the enemy will jeopardize five gunboats")))) 15 | 16 | (deftest another-missing-character-x 17 | (is 18 | (false? 19 | (pangram? "the quick brown fish jumps over the lazy dog")))) 20 | 21 | (deftest with-underscores 22 | (is (pangram? "the_quick_brown_fox_jumps_over_the_lazy_dog"))) 23 | 24 | (deftest with-numbers 25 | (is (pangram? "the 1 quick brown fox jumps over the 2 lazy dogs"))) 26 | 27 | (deftest missing-letters-replaced-by-numbers 28 | (is 29 | (false? 30 | (pangram? "7h3 qu1ck brown fox jumps ov3r 7h3 lazy dog")))) 31 | 32 | (deftest mixed-case-and-punctuation 33 | (is (pangram? "\"Five quacking Zephyrs jolt my wax bed.\""))) 34 | 35 | (deftest upper-and-lower-not-counted-separately 36 | (is 37 | (false? 38 | (pangram? "the quick brown fox jumps over with lazy FX")))) 39 | -------------------------------------------------------------------------------- /pov/README.md: -------------------------------------------------------------------------------- 1 | # POV 2 | 3 | Reparent a graph on a selected node. 4 | 5 | This exercise is all about re-orientating a graph to see things from a different 6 | point of view. For example family trees are usually presented from the 7 | ancestor's perspective: 8 | 9 | ```text 10 | +------0------+ 11 | | | | 12 | +-1-+ +-2-+ +-3-+ 13 | | | | | | | 14 | 4 5 6 7 8 9 15 | ``` 16 | 17 | But the same information can be presented from the perspective of any other node 18 | in the graph, by pulling it up to the root and dragging its relationships along 19 | with it. So the same graph from 6's perspective would look like: 20 | 21 | ```text 22 | 6 23 | | 24 | +-----2-----+ 25 | | | 26 | 7 +-----0-----+ 27 | | | 28 | +-1-+ +-3-+ 29 | | | | | 30 | 4 5 8 9 31 | ``` 32 | 33 | This lets us more simply describe the paths between two nodes. So for example 34 | the path from 6-9 (which in the first graph goes up to the root and then down to 35 | a different leaf node) can be seen to follow the path 6-2-0-3-9 36 | 37 | This exercise involves taking an input graph and re-orientating it from the point 38 | of view of one of the nodes. 39 | ## Source 40 | 41 | Adaptation of exercise from 4clojure [https://www.4clojure.com/](https://www.4clojure.com/) 42 | -------------------------------------------------------------------------------- /anagram/test/anagram_test.clj: -------------------------------------------------------------------------------- 1 | (ns anagram-test 2 | (:require [clojure.test :refer [deftest is]] 3 | anagram)) 4 | 5 | (deftest no-matches 6 | (is (= [] 7 | (anagram/anagrams-for "diaper" ["hello" "world" "zombies" "pants"])))) 8 | 9 | (deftest detect-simple-anagram 10 | (is (= ["tan"] (anagram/anagrams-for "ant" ["tan" "stand" "at"])))) 11 | 12 | (deftest does-not-confuse-different-duplicates 13 | (is (= [] (anagram/anagrams-for "galea" ["eagle"])))) 14 | 15 | (deftest eliminate-anagram-subsets 16 | (is (= [] (anagram/anagrams-for "good" ["dog" "goody"])))) 17 | 18 | (deftest detect-anagram 19 | (is (= ["inlets"] 20 | (let [coll ["enlists" "google" "inlets" "banana"]] 21 | (anagram/anagrams-for "listen" coll))))) 22 | 23 | (deftest multiple-anagrams 24 | (is (= ["gallery" "regally" "largely"] 25 | (let [coll ["gallery" "ballerina" "regally" 26 | "clergy" "largely" "leading"]] 27 | (anagram/anagrams-for "allergy" coll))))) 28 | 29 | (deftest case-insensitive-anagrams 30 | (is (= ["Carthorse"] 31 | (let [coll ["cashregister" "Carthorse" "radishes"]] 32 | (anagram/anagrams-for "Orchestra" coll))))) 33 | 34 | (deftest word-is-not-own-anagram 35 | (is (= [] (anagram/anagrams-for "banana" ["banana"])))) 36 | 37 | (deftest capital-word-is-not-own-anagram 38 | (is (= [] (anagram/anagrams-for "BANANA" ["banana"])))) 39 | -------------------------------------------------------------------------------- /phone-number/test/phone_number_test.clj: -------------------------------------------------------------------------------- 1 | (ns phone-number-test 2 | (:require [clojure.test :refer [deftest is]] 3 | phone-number)) 4 | 5 | (deftest cleans-number 6 | (is (= "1234567890" (phone-number/number "(123) 456-7890")))) 7 | 8 | (deftest cleans-number-with-dots 9 | (is (= "5558675309" (phone-number/number "555.867.5309")))) 10 | 11 | (deftest valid-when-11-digits-and-first-is-1 12 | (is (= "9876543210" (phone-number/number "19876543210")))) 13 | 14 | (deftest invalid-when-11-digits 15 | (is (= "0000000000" (phone-number/number "21234567890")))) 16 | 17 | (deftest invalid-when-9-digits 18 | (is (= "0000000000" (phone-number/number "123456789")))) 19 | 20 | (deftest area-code 21 | (is (= "123" (phone-number/area-code "1234567890")))) 22 | 23 | (deftest area-code-with-dots 24 | (is (= "555" (phone-number/area-code "555.867.5309")))) 25 | 26 | (deftest area-code-with-parentheses 27 | (is (= "987" (phone-number/area-code "(987) 654-3210")))) 28 | 29 | (deftest area-code-with-full-us-phone-number 30 | (is (= "123" (phone-number/area-code "11234567890")))) 31 | 32 | (deftest pretty-print 33 | (is (= "(123) 456-7890" (phone-number/pretty-print "1234567890")))) 34 | 35 | (deftest pretty-print-with-dots 36 | (is (= "(555) 867-5309" (phone-number/pretty-print "555.867.5309")))) 37 | 38 | (deftest pretty-print-with-full-us-phone-number 39 | (is (= "(987) 654-3210" (phone-number/pretty-print "19876543210")))) 40 | -------------------------------------------------------------------------------- /rna-transcription/src/rna_transcription.clj: -------------------------------------------------------------------------------- 1 | (ns rna-transcription 2 | (:require 3 | [clojure.string :as string] 4 | [clojure.spec.alpha :as spec])) 5 | 6 | (def dna-nucleotide->rna-nucleotide 7 | "DNA to RNA transcription mappings" 8 | {\G \C 9 | \C \G 10 | \T \A 11 | \A \U}) 12 | 13 | (defn to-rna 14 | "Transcribe each nucleotide from a DNA strand into its RNA complement 15 | Arguments: string representing DNA strand 16 | Return: string representing RNA strand" 17 | [dna] 18 | (string/join 19 | (map #(or (dna-nucleotide->rna-nucleotide %) 20 | (throw (AssertionError. "Unknown nucleotide"))) 21 | dna))) 22 | 23 | ;; A pre-condition will throw an assertion error if its expression fails 24 | 25 | #_(defn to-rna [dna] 26 | {:pre [(every? dna-complement dna)]} 27 | (->> dna 28 | (map dna-complement) 29 | (apply str))) 30 | 31 | (comment 32 | (to-rna "GATTAGA") 33 | ;; => "CUAAUCU" 34 | 35 | (to-rna "GX") 36 | 37 | (def dna->rna-map (zipmap "GCTA" 38 | "CGAU")) 39 | 40 | dna->rna-map 41 | 42 | (spec/def ::dna string?) 43 | 44 | (defn to-rna [dna] 45 | (let [rna (keep {\G \C, \C \G, \T \A, \A \U} dna)] 46 | (or (assert (spec/valid? ::dna rna)) rna)))) 47 | 48 | (comment 49 | (def value 50 | (map inc [1 2 3])) 51 | 52 | ;; , e w or , e r to evaluate a name/symbol 53 | value 54 | 55 | (map inc [1 2 3])) 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /isbn-verifier/test/isbn_verifier_test.clj: -------------------------------------------------------------------------------- 1 | (ns isbn-verifier-test 2 | (:require [clojure.test :refer [deftest is]] 3 | [isbn-verifier :refer [isbn?]])) 4 | 5 | (deftest valid-isbn-number 6 | (is (= true (isbn? "3-598-21508-8")))) 7 | 8 | (deftest invalid-isbn-check-digit 9 | (is (= false (isbn? "3-598-21508-9")))) 10 | 11 | (deftest valid-isbn-number-with-a-check-digit-of-10 12 | (is (= true (isbn? "3-598-21507-X")))) 13 | 14 | (deftest check-digit-is-a-character-other-than-X 15 | (is (= false (isbn? "3-598-21507-A")))) 16 | 17 | (deftest invalid-character-in-isbn 18 | (is (= false (isbn? "3-598-2K507-0")))) 19 | 20 | (deftest X-is-only-valid-as-a-check-digit 21 | (is (= false (isbn? "3-598-2X507-9")))) 22 | 23 | (deftest valid-isbn-without-separating-dashes 24 | (is (= true (isbn? "3598215088")))) 25 | 26 | (deftest isbn-without-separating-dashes-and-X-as-check-digit 27 | (is (= true (isbn? "359821507X")))) 28 | 29 | (deftest isbn-without-check-digit-and-dashes 30 | (is (= false (isbn? "359821507")))) 31 | 32 | (deftest too-long-isbn-and-no-dashes 33 | (is (= false (isbn? "3598215078X")))) 34 | 35 | (deftest too-short-isbn 36 | (is (= false (isbn? "00")))) 37 | 38 | (deftest isbn-without-check-digit 39 | (is (= false (isbn? "3-598-21507")))) 40 | 41 | (deftest too-long-isbn 42 | (is (= false (isbn? "3-598-21507-XX")))) 43 | 44 | (deftest check-digit-of-X-should-not-be-used-for-0 45 | (is (= false (isbn? "3-598-21515-X")))) 46 | -------------------------------------------------------------------------------- /phone-number/README.md: -------------------------------------------------------------------------------- 1 | # Phone Number 2 | 3 | Clean up user-entered phone numbers so that they can be sent SMS messages. 4 | 5 | The **North American Numbering Plan (NANP)** is a telephone numbering system used by many countries in North America like the United States, Canada or Bermuda. All NANP-countries share the same international country code: `1`. 6 | 7 | NANP numbers are ten-digit numbers consisting of a three-digit Numbering Plan Area code, commonly known as *area code*, followed by a seven-digit local number. The first three digits of the local number represent the *exchange code*, followed by the unique four-digit number which is the *subscriber number*. 8 | 9 | The format is usually represented as 10 | 11 | ```text 12 | (NXX)-NXX-XXXX 13 | ``` 14 | 15 | where `N` is any digit from 2 through 9 and `X` is any digit from 0 through 9. 16 | 17 | Your task is to clean up differently formatted telephone numbers by removing punctuation and the country code (1) if present. 18 | 19 | For example, the inputs 20 | - `+1 (613)-995-0253` 21 | - `613-995-0253` 22 | - `1 613 995 0253` 23 | - `613.995.0253` 24 | 25 | should all produce the output 26 | 27 | `6139950253` 28 | 29 | **Note:** As this exercise only deals with telephone numbers used in NANP-countries, only 1 is considered a valid country code. 30 | 31 | 32 | ## Source 33 | 34 | Event Manager by JumpstartLab [http://tutorials.jumpstartlab.com/projects/eventmanager.html](http://tutorials.jumpstartlab.com/projects/eventmanager.html) 35 | -------------------------------------------------------------------------------- /hamming/README.md: -------------------------------------------------------------------------------- 1 | # Hamming 2 | 3 | Calculate the Hamming Distance between two DNA strands. 4 | 5 | Your body is made up of cells that contain DNA. Those cells regularly wear out and need replacing, which they achieve by dividing into daughter cells. In fact, the average human body experiences about 10 quadrillion cell divisions in a lifetime! 6 | 7 | When cells divide, their DNA replicates too. Sometimes during this process mistakes happen and single pieces of DNA get encoded with the incorrect information. If we compare two strands of DNA and count the differences between them we can see how many mistakes occurred. This is known as the "Hamming Distance". 8 | 9 | We read DNA using the letters C,A,G and T. Two strands might look like this: 10 | 11 | GAGCCTACTAACGGGAT 12 | CATCGTAATGACGGCCT 13 | ^ ^ ^ ^ ^ ^^ 14 | 15 | They have 7 differences, and therefore the Hamming Distance is 7. 16 | 17 | The Hamming Distance is useful for lots of things in science, not just biology, so it's a nice phrase to be familiar with :) 18 | 19 | ## Implementation notes 20 | 21 | The Hamming distance is only defined for sequences of equal length, so an attempt to calculate it between sequences of different lengths should not work. 22 | 23 | The general handling of this situation (e.g., raising an exception vs returning a special value) may differ between languages. 24 | 25 | 26 | ## Source 27 | 28 | The Calculating Point Mutations problem at Rosalind [http://rosalind.info/problems/hamm/](http://rosalind.info/problems/hamm/) 29 | -------------------------------------------------------------------------------- /armstrong-numbers/test/armstrong_numbers_test.clj: -------------------------------------------------------------------------------- 1 | (ns armstrong-numbers-test 2 | (:require [clojure.test :refer [deftest is testing]] 3 | [armstrong-numbers :refer [armstrong?]])) 4 | 5 | (deftest armstrong-number-0 6 | (testing "Zero is an Armstrong number" 7 | (is (armstrong? 0)))) 8 | 9 | (deftest armstrong-number-5 10 | (testing "Single digit numbers are Armstrong numbers" 11 | (is (armstrong? 5)))) 12 | 13 | (deftest not-armstrong-number-10 14 | (testing "There are no 2 digit Armstrong numbers" 15 | (is (not (armstrong? 10))))) 16 | 17 | (deftest armstrong-number-153 18 | (testing "Three digit number that is an Armstrong number" 19 | (is (armstrong? 153)))) 20 | 21 | (deftest not-armstrong-number-100 22 | (testing "Three digit number that is not an Armstrong number" 23 | (is (not (armstrong? 100))))) 24 | 25 | (deftest armstrong-number-9474 26 | (testing "Four digit number that is an Armstrong number" 27 | (is (armstrong? 9474)))) 28 | 29 | (deftest not-armstrong-number-9475 30 | (testing "Four digit number that is not an Armstrong number" 31 | (is (not (armstrong? 9475))))) 32 | 33 | (deftest armstrong-number-9926315 34 | (testing "Seven digit number that is an Armstrong number" 35 | (is (armstrong? 9926315)))) 36 | 37 | (deftest not-armstrong-number-9926314 38 | (testing "Seven digit number that is not an Armstrong number" 39 | (is (not (armstrong? 9926314))))) 40 | 41 | (deftest armstrong-number-21897142587612075 42 | (testing "Seventeen digit number that is an Armstrong number" 43 | (is (armstrong? 21897142587612075)))) 44 | -------------------------------------------------------------------------------- /space-age/test/space_age_test.clj: -------------------------------------------------------------------------------- 1 | (ns space-age-test 2 | (:require [clojure.test :refer [deftest is]] 3 | space-age)) 4 | 5 | (defn- rounds-to 6 | [expected actual] 7 | (is (= (Math/round (* 100.0 expected)) 8 | (Math/round (* 100.0 actual))))) 9 | 10 | (deftest age-in-earth-years 11 | (rounds-to 31.69 (space-age/on-earth 1000000000))) 12 | 13 | (deftest age-in-mercury-years 14 | (let [seconds 2134835688] 15 | (rounds-to 67.65 (space-age/on-earth seconds)) 16 | (rounds-to 280.88 (space-age/on-mercury seconds)))) 17 | 18 | (deftest age-in-venus-years 19 | (let [seconds 189839836] 20 | (rounds-to 6.02 (space-age/on-earth seconds)) 21 | (rounds-to 9.78 (space-age/on-venus seconds)))) 22 | 23 | (deftest age-on-mars 24 | (let [seconds 2329871239] 25 | (rounds-to 73.83 (space-age/on-earth seconds)) 26 | (rounds-to 39.25 (space-age/on-mars seconds)))) 27 | 28 | (deftest age-on-jupiter 29 | (let [seconds 901876382] 30 | (rounds-to 28.58 (space-age/on-earth seconds)) 31 | (rounds-to 2.41 (space-age/on-jupiter seconds)))) 32 | 33 | (deftest age-on-saturn 34 | (let [seconds 3000000000] 35 | (rounds-to 95.06 (space-age/on-earth seconds)) 36 | (rounds-to 3.23 (space-age/on-saturn seconds)))) 37 | 38 | (deftest age-on-uranus 39 | (let [seconds 3210123456] 40 | (rounds-to 101.72 (space-age/on-earth seconds)) 41 | (rounds-to 1.21 (space-age/on-uranus seconds)))) 42 | 43 | (deftest age-on-neptune 44 | (let [seconds 8210123456] 45 | (rounds-to 260.16 (space-age/on-earth seconds)) 46 | (rounds-to 1.58 (space-age/on-neptune seconds)))) 47 | -------------------------------------------------------------------------------- /armstrong-numbers/mentor-feedback.md: -------------------------------------------------------------------------------- 1 | Thank you for submitting this solution. 2 | 3 | This is a good solution to the challenge and counting strings is a commonly submitted approach. 4 | 5 | It is possible to count the number of digits in the number using math. The `quot`, `rem` and `mod` functions are common core functions for working with numbers. 6 | 7 | If `quot` of a number to base 10 is iterated over the number, then each digit of the number can be returned. Be careful if using a function like iterate as this can generate an infinite sequence (blowing up the heap), if some context is not given. 8 | 9 | The submitted solution has many nested expressions, which can make the code a little harder to parse for the human. One approach to this is to tweak the formatting (hopefully that shows up okay on exercism), 10 | 11 | ``` 12 | (defn armstrong? [num] 13 | (== num 14 | (reduce + 0 15 | (map 16 | #(reduce * (repeat (count (str num)) 17 | (Character/digit % 10))) 18 | (str num))))) 19 | 20 | ``` 21 | 22 | Another approach is to use the threading macros 23 | [https://clojure.org/guides/threading_macros](https://clojure.org/guides/threading_macros) 24 | 25 | This syntax is very useful, especially when showing a sequence of processing steps / transformations 26 | There are some examples of using threading macros here 27 | [https://github.com/practicalli/clojure-through-code/blob/master/src/clojure_through_code/hhgttg-book-common-words.clj](https://github.com/practicalli/clojure-through-code/blob/master/src/clojure_through_code/hhgttg-book-common-words.clj) 28 | -------------------------------------------------------------------------------- /bank-account/src/bank_account.clj: -------------------------------------------------------------------------------- 1 | ;; --------------------------------------------------------- 2 | ;; Bank account 3 | ;; 4 | ;; Using software transactional memory (atom) 5 | ;; --------------------------------------------------------- 6 | 7 | 8 | (ns bank-account) 9 | 10 | ;; --------------------------------------------------------- 11 | (def bank-accounts 12 | "All accounts for the bank 13 | Each account has a unique id and a numeric value" 14 | (atom {})) 15 | ;; --------------------------------------------------------- 16 | 17 | ;; --------------------------------------------------------- 18 | (defn open-account 19 | "Open an account with a zero balance." 20 | [] 21 | (let [account-id (java.util.UUID/randomUUID)] 22 | (swap! bank-accounts assoc account-id 0) 23 | account-id)) 24 | 25 | (defn close-account [account-id] 26 | (swap! bank-accounts dissoc account-id)) 27 | 28 | (defn get-balance [account-id] 29 | (get @bank-accounts account-id)) 30 | 31 | (defn update-balance [account-id credit] 32 | (swap! bank-accounts update account-id #(+ % credit))) 33 | ;; --------------------------------------------------------- 34 | 35 | ;; --------------------------------------------------------- 36 | (comment 37 | ;; Experimenting with code in the REPL 38 | (open-account) 39 | 40 | (= 0 (-> (open-account) 41 | (get-balance))) 42 | 43 | (deref bank-accounts) 44 | ;; => {#uuid "af0747fc-86bf-496a-8cc1-efb917887213" 0} 45 | 46 | (update-balance #uuid "af0747fc-86bf-496a-8cc1-efb917887213" 10) 47 | ;; => {#uuid "af0747fc-86bf-496a-8cc1-efb917887213" 10} 48 | 49 | (update-balance #uuid "af0747fc-86bf-496a-8cc1-efb917887213" -10)) 50 | ;; => {#uuid "af0747fc-86bf-496a-8cc1-efb917887213" 0} 51 | ;; --------------------------------------------------------- 52 | -------------------------------------------------------------------------------- /anagram/src/design_journal.clj: -------------------------------------------------------------------------------- 1 | (ns design-journal) 2 | 3 | ;; Rich comment block with redefined vars ignored 4 | #_{:clj-kondo/ignore [:redefined-var]} 5 | (comment 6 | 7 | ;;;;;;;; 8 | ;; Well done using comp and filter. 9 | ;; Perhaps the function passed to filter could be an auxiliary function. It may read better. 10 | ;; Using partial can be helpful to pass some args to the function. 11 | 12 | (ns anagram) 13 | (def lower clojure.string/lower-case) 14 | 15 | 16 | (defn anagrams-for [word prospect-list] 17 | (let [w ((comp sort lower) word)] 18 | (filter (fn [prospect] (let [p (lower prospect)] 19 | (and (not= (lower word) p) 20 | (= w (sort p))))) prospect-list))) 21 | 22 | 23 | ;;;;;;;; 24 | ;; take advantage that word doesn't change and calculate beforehand the sorted and lowercase version. 25 | 26 | (ns anagram) 27 | (def lower clojure.string/lower-case) 28 | 29 | (defn- anagram? [word prospect] 30 | (let [w ((comp sort lower) word) 31 | p (lower prospect)] 32 | (and (not= (lower word) p) 33 | (= w (sort p))))) 34 | 35 | (defn anagrams-for [word prospect-list] 36 | (filter (partial anagram? word) prospect-list)) 37 | 38 | 39 | ;;;;;;;; 40 | ;; make word lower and sort it only once in the main function 41 | 42 | (ns anagram) 43 | (def lower clojure.string/lower-case) 44 | 45 | (defn- anagram? [word word-lower word-sorted prospect] 46 | (let [p (lower prospect)] 47 | (and (not= word-lower p) 48 | (= word-sorted (sort p))))) 49 | 50 | (defn anagrams-for [word prospect-list] 51 | (let [word-lower (lower word) 52 | word-sorted (sort word-lower)] 53 | (filter (partial anagram? word word-lower word-sorted) prospect-list))) 54 | 55 | 56 | ) ;; End of rich comment block 57 | -------------------------------------------------------------------------------- /etl/README.md: -------------------------------------------------------------------------------- 1 | # ETL 2 | 3 | We are going to do the `Transform` step of an Extract-Transform-Load. 4 | 5 | > NOTE: solution not shared yet. 6 | 7 | ### Challenge 8 | 9 | Extract-Transform-Load (ETL) is a fancy way of saying, "We have some crufty, legacy data over in this system, and now we need it in this shiny new system over here, so 10 | we're going to migrate this." 11 | 12 | (Typically, this is followed by, "We're only going to need to run this 13 | once." That's then typically followed by much forehead slapping and 14 | moaning about how stupid we could possibly be.) 15 | 16 | ### The goal 17 | 18 | We're going to extract some scrabble scores from a legacy system. 19 | 20 | The old system stored a list of letters per score: 21 | 22 | - 1 point: "A", "E", "I", "O", "U", "L", "N", "R", "S", "T", 23 | - 2 points: "D", "G", 24 | - 3 points: "B", "C", "M", "P", 25 | - 4 points: "F", "H", "V", "W", "Y", 26 | - 5 points: "K", 27 | - 8 points: "J", "X", 28 | - 10 points: "Q", "Z", 29 | 30 | The shiny new scrabble system instead stores the score per letter, which makes it much faster and easier to calculate the score for a word. 31 | 32 | It also stores the letters in lower-case regardless of the case of the input letters: 33 | 34 | - "a" is worth 1 point. 35 | - "b" is worth 3 points. 36 | - "c" is worth 3 points. 37 | - "d" is worth 2 points. 38 | - Etc. 39 | 40 | Your mission, should you choose to accept it, is to transform the legacy data format to the shiny new format. 41 | 42 | ### Notes 43 | 44 | A final note about scoring, Scrabble is played around the world in a variety of languages, each with its own unique scoring table. 45 | 46 | For example, an "E" is scored at 2 in the Māori-language version of the game while being scored at 4 in the Hawaiian-language version. 47 | 48 | 49 | ## Source 50 | 51 | The Jumpstart Lab team [http://jumpstartlab.com](http://jumpstartlab.com) 52 | -------------------------------------------------------------------------------- /hello-world/README.md: -------------------------------------------------------------------------------- 1 | # Hello World 2 | 3 | The classical introductory exercise. Just say "Hello, World!". 4 | 5 | ["Hello, World!"](http://en.wikipedia.org/wiki/%22Hello,_world!%22_program) is 6 | the traditional first program for beginning programming in a new language 7 | or environment. 8 | 9 | The objectives are simple: 10 | 11 | - Write a function that returns the string "Hello, World!". 12 | - Run the test suite and make sure that it succeeds. 13 | - Submit your solution and check it at the website. 14 | 15 | If everything goes well, you will be ready to fetch your first real exercise. 16 | 17 | ### Project Structure 18 | 19 | Clojure exercises in exercism use [leiningen](http://leiningen.org/) to configure and run your code 20 | and use [leiningen standard directory structure](https://github.com/technomancy/leiningen/blob/master/doc/TUTORIAL.md#directory-layout). 21 | 22 | You will find a test file named `hello_world_test.clj` inside `test` directory. 23 | Write your code in `src/hello_world.clj`. It should use the namespace `hello-world` so that tests can pick it up. 24 | 25 | ### Running tests 26 | 27 | Run the tests using `lein test` command and make them pass: 28 | 29 | ``` 30 | $ lein test 31 | 32 | lein test hello-world-test 33 | 34 | Ran 1 tests containing 1 assertions. 35 | 0 failures, 0 errors. 36 | ``` 37 | 38 | Then submit the exercise using: 39 | 40 | ``` 41 | $ exercism submit src/hello_world.clj 42 | ``` 43 | 44 | For more detailed instructions and learning resources refer [exercism's clojure language page](http://exercism.io/languages/clojure). 45 | 46 | ## Source 47 | 48 | This is an exercise to introduce users to using Exercism [http://en.wikipedia.org/wiki/%22Hello,_world!%22_program](http://en.wikipedia.org/wiki/%22Hello,_world!%22_program) 49 | 50 | ## Submitting Incomplete Solutions 51 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 52 | -------------------------------------------------------------------------------- /bank-account/test/bank_account_test.clj: -------------------------------------------------------------------------------- 1 | (ns bank-account-test 2 | (:require 3 | [clojure.test :refer [deftest testing is use-fixtures]] 4 | [bank-account])) 5 | 6 | ;; Tests force REPL disconnect in CIDER 7 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 8 | 9 | (defn shutdown-agents-fixture [f] 10 | (f) 11 | (shutdown-agents)) 12 | 13 | (use-fixtures :once shutdown-agents-fixture) 14 | 15 | (deftest initial-account-state 16 | (testing "Accounts are opened with a balance of 0" 17 | (is (= 0 (-> (bank-account/open-account) 18 | (bank-account/get-balance)))))) 19 | 20 | #_(bank-account/get-balance 21 | (bank-account/open-account)) 22 | 23 | (deftest increment-and-get-balance 24 | (testing "Adding money to the account works" 25 | (let [account (bank-account/open-account)] 26 | (is (= 0 (bank-account/get-balance account))) 27 | (bank-account/update-balance account 10) 28 | (is (= 10 (bank-account/get-balance account)))))) 29 | 30 | (deftest increment-decrement-and-get-balance 31 | (testing "Taking money out of the account works" 32 | (let [account (bank-account/open-account)] 33 | (is (= 0 (bank-account/get-balance account))) 34 | (bank-account/update-balance account 10) 35 | (is (= 10 (bank-account/get-balance account))) 36 | (bank-account/update-balance account -10) 37 | (is (= 0 (bank-account/get-balance account)))))) 38 | 39 | (deftest closed-accounts-are-nil 40 | (testing "Closing an account makes it nil" 41 | (let [account (bank-account/open-account)] 42 | (bank-account/close-account account) 43 | (is (nil? (bank-account/get-balance account)))))) 44 | 45 | (deftest check-concurrent-access 46 | (testing "The account can handle parallel access" 47 | (let [account (bank-account/open-account) 48 | add-10 #(bank-account/update-balance account 10)] 49 | (doall (pcalls add-10 add-10 add-10 add-10 add-10)) 50 | (is (= 50 (bank-account/get-balance account)))))) 51 | -------------------------------------------------------------------------------- /pov/src/design_journal.clj: -------------------------------------------------------------------------------- 1 | (ns design-journal) 2 | 3 | (comment 4 | 5 | (defn look-up [hidden graph] 6 | (let [exists? (partial some) findHidden (partial = hidden) 7 | vect (comp (partial apply vector) (partial filter vector?)) 8 | nVect (comp (partial apply vector) (partial filter (complement vector?)))] 9 | 10 | (if (not (exists? findHidden (flatten graph))) nil 11 | 12 | (if (exists? findHidden graph) 13 | [(into [hidden] (remove findHidden graph))] 14 | 15 | (as-> graph g (vect g) 16 | (filter (comp (partial exists? findHidden) flatten) g) 17 | (reduce into g) 18 | (look-up hidden g) 19 | (conj g (->> graph 20 | vect 21 | (remove (comp (partial exists? findHidden) flatten)) 22 | (apply conj (nVect graph))))))))) 23 | 24 | (defn of [hidden graph] 25 | (let [graph (look-up hidden graph)] 26 | (if (= graph nil) nil 27 | (->> graph 28 | reverse 29 | (reduce #(conj %2 %1)))))) 30 | 31 | (defn locate-path [end graph] 32 | (let [exists? (partial some (partial = end))] 33 | (cond 34 | (exists? graph) end 35 | (-> graph ((comp exists? flatten)) not) nil 36 | :else (->> graph (filter vector?) 37 | (filter (comp exists? flatten)) 38 | (reduce into) 39 | (locate-path end) 40 | (conj [(first graph)]))))) 41 | 42 | (defn path-from-to [start end graph] 43 | (let [graph (of start graph) 44 | route (locate-path end graph)] 45 | (if (= route nil) nil 46 | (->> route flatten (apply vector)))))) 47 | -------------------------------------------------------------------------------- /armstrong-numbers/src/armstrong_numbers.clj: -------------------------------------------------------------------------------- 1 | (ns armstrong-numbers) 2 | 3 | ;; Using floating point numbers leads to madness 4 | 5 | 6 | ;; Definitions 7 | ;; expt returns base-number raised to the power power-number. 8 | ;; https://en.wikipedia.org/wiki/Exponential_function 9 | 10 | (ns armstrong-numbers) 11 | 12 | (defn expt [base pow] 13 | (reduce * 1 (repeat pow base))) 14 | 15 | (defn armstrong? [n] 16 | (let [digits (map (comp read-string str) (str n)) 17 | l (count digits)] 18 | (= n (reduce + (map #(expt % l) digits))))) 19 | 20 | 21 | 22 | 23 | (defn raise-to-power 24 | [number power] 25 | (reduce * (repeat power number))) 26 | 27 | (defn armstrong? 28 | [number] 29 | (let [;; Individual digits from the number 30 | digits (map #(rem % 10) 31 | (take-while pos? (iterate #(quot % 10) number))) 32 | 33 | ;; number of digits, used as the power 34 | power (count digits)] 35 | 36 | (->> digits 37 | (map #(raise-to-power % power)) 38 | (reduce +) 39 | (= number)))) 40 | 41 | 42 | 43 | 44 | 45 | 46 | (defn digits [n] 47 | (->> n 48 | (iterate #(quot % 10)) 49 | (take-while pos?) 50 | (mapv #(mod % 10)) 51 | (rseq)) 52 | ) 53 | 54 | (defn armstrong? [n] 55 | (let [exp-length #(reduce * (repeat (count (digits n)) %))] 56 | (->> n 57 | (digits) 58 | (map exp-length) 59 | (reduce +) 60 | (= n)))) 61 | 62 | (armstrong? 153) 63 | 64 | 65 | (mod 15 10) 66 | (rem 15 10) 67 | 68 | 69 | 70 | (defn armstrong? [num] 71 | (let [exp (fn [e n] (reduce * (repeat e n)))] 72 | (= num (loop [n num 73 | digits [0] 74 | e 0] 75 | (if (>= n 1) 76 | (recur (quot n 10) 77 | (conj digits (rem n 10)) 78 | (inc e)) 79 | (->> digits 80 | (map (partial exp (max e 1))) 81 | (reduce +))))))) 82 | 83 | (armstrong? 153) 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Exercism - Challenges in Clojure 2 | 3 | Exercism is a website providing mentorship and code challenges as a means to help anyone learn how to write code. There are 50 different language tracks available. 4 | 5 | The [practicalli/exercism-clojure-guides repository](https://github.com/practicalli/exercism-clojure-guides) provide a design journal and submitted solutions to the challenges from the [Exercism Clojure Track](https://exercism.io/my/tracks/clojure). 6 | 7 | ## Using this guide 8 | Fork the [practicalli/exercism-clojure-guides repository](https://github.com/practicalli/exercism-clojure-guides) if you wish to add your own changes to the solutions, or clone the project to your computer. 9 | 10 | Projects are defined in each top level directory, with a simple `deps.edn` file added so projects can also be run with Clojure CLI tools as well as Leiningen. 11 | 12 | Open the project in your favorite Clojure aware editor and start a REPL, or run a terminal REPL. 13 | 14 | Evaluate the code and experiment. 15 | 16 | ## Copywrite / License 17 | 18 | Creative Commons License
practicalli/exercism-clojure-guides by Practicalli is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
Based on a work at https://github.com/practicalli/exercism-clojure-guides.
Permissions beyond the scope of this license may be available at https://practicalli.github.io/#contact. 19 | -------------------------------------------------------------------------------- /beer-song/test/beer_song_test.clj: -------------------------------------------------------------------------------- 1 | (ns beer-song-test 2 | (:require [clojure.test :refer [deftest is]] 3 | beer-song)) 4 | 5 | (def verse-8 6 | (str "8 bottles of beer on the wall, 8 bottles of beer.\n" 7 | "Take one down and pass it around, 7 bottles of beer on the wall.\n")) 8 | 9 | (def verse-2 10 | (str "2 bottles of beer on the wall, 2 bottles of beer.\n" 11 | "Take one down and pass it around, 1 bottle of beer on the wall.\n")) 12 | 13 | (def verse-1 14 | (str "1 bottle of beer on the wall, 1 bottle of beer.\n" 15 | "Take it down and pass it around, no more bottles of beer on the wall.\n")) 16 | 17 | (def verse-0 18 | (str "No more bottles of beer on the wall, no more bottles of beer.\n" 19 | "Go to the store and buy some more, 99 bottles of beer on the wall.\n")) 20 | 21 | (def song-8-6 22 | (str "8 bottles of beer on the wall, 8 bottles of beer.\n" 23 | "Take one down and pass it around, 7 bottles of beer on the wall.\n\n" 24 | "7 bottles of beer on the wall, 7 bottles of beer.\n" 25 | "Take one down and pass it around, 6 bottles of beer on the wall.\n\n" 26 | "6 bottles of beer on the wall, 6 bottles of beer.\n" 27 | "Take one down and pass it around, 5 bottles of beer on the wall.\n")) 28 | 29 | (def song-3-0 30 | (str "3 bottles of beer on the wall, 3 bottles of beer.\n" 31 | "Take one down and pass it around, 2 bottles of beer on the wall.\n\n" 32 | "2 bottles of beer on the wall, 2 bottles of beer.\n" 33 | "Take one down and pass it around, 1 bottle of beer on the wall.\n\n" 34 | "1 bottle of beer on the wall, 1 bottle of beer.\n" 35 | "Take it down and pass it around, no more bottles of beer on the wall.\n\n" 36 | "No more bottles of beer on the wall, no more bottles of beer.\n" 37 | "Go to the store and buy some more, 99 bottles of beer on the wall.\n")) 38 | 39 | (deftest test-verse 40 | (is (= verse-8 (beer-song/verse 8))) 41 | (is (= verse-2 (beer-song/verse 2))) 42 | (is (= verse-1 (beer-song/verse 1))) 43 | (is (= verse-0 (beer-song/verse 0)))) 44 | 45 | (deftest test-song 46 | (is (= song-8-6 (beer-song/sing 8 6))) 47 | (is (= song-3-0 (beer-song/sing 3)))) 48 | -------------------------------------------------------------------------------- /clock/src/design_journal.clj: -------------------------------------------------------------------------------- 1 | ;; --------------------------------------------------------- 2 | ;; Heading summary title 3 | ;; 4 | ;; Brief description 5 | ;; --------------------------------------------------------- 6 | 7 | 8 | ;; --------------------------------------------------------- 9 | (ns design-journal) 10 | ;; --------------------------------------------------------- 11 | 12 | ;; --------------------------------------------------------- 13 | (defn clock->string [clock] 14 | (let [h (:hours clock) 15 | m (:minutes clock)] 16 | (format "%02d:%02d" h m))) 17 | 18 | (defn minutes [clock] 19 | (+ (:minutes clock) (* (:hours clock) 60))) 20 | 21 | (defn adjusted-minutes [n] 22 | (mod (+ n 1440) 1440)) 23 | 24 | (defn add-time [clock time] 25 | (let [m (adjusted-minutes (+ time (minutes clock)))] 26 | {:hours (quot m 60) :minutes (mod m 60)})) 27 | 28 | (defn clock 29 | ([] {:hours 0 :minutes 0}) 30 | ([hours minutes] (add-time (clock) (+ minutes (* hours 60))))) 31 | ;; --------------------------------------------------------- 32 | 33 | ;; --------------------------------------------------------- 34 | ;; Mentor Feedback 35 | 36 | ;; This is a very clear solution and easy to understand. I particularly like the use of the polymorphic `clock` function. 37 | 38 | ;; Using the let function helps break down the complexity of a function into smaller pieces. However, it also introduces temporary local names. It can also be challenging to think of meaningful local names and obscure names are a candidate for dropping the let or refactoring the code. 39 | 40 | ;; Sometimes it is more efficient to simply use the values inline, especially when those values are used only once. 41 | 42 | ;; It can be easier to use the let initially and then remove the let after the function is working if the let bindings are not required. 43 | 44 | ;; For example, the `clock->string` function could be written as follows without loosing any clarity 45 | 46 | ;; ``` 47 | ;; (defn clock->string [clock] 48 | ;; (format "%02d:%02d" (:hours clock) (:minutes clock))) 49 | ;; ``` 50 | 51 | ;; The `add-time` function uses the let value in several places, so makes sense to keep it. 52 | ;; --------------------------------------------------------- 53 | -------------------------------------------------------------------------------- /isbn-verifier/README.md: -------------------------------------------------------------------------------- 1 | # ISBN Verifier 2 | 3 | The [ISBN-10 verification process](https://en.wikipedia.org/wiki/International_Standard_Book_Number) is used to validate book identification 4 | numbers. These normally contain dashes and look like: `3-598-21508-8` 5 | 6 | ## ISBN 7 | 8 | The ISBN-10 format is 9 digits (0 to 9) plus one check character (either a digit or an X only). In the case the check character is an X, this represents the value '10'. These may be communicated with or without hyphens, and can be checked for their validity by the following formula: 9 | 10 | ``` 11 | (x1 * 10 + x2 * 9 + x3 * 8 + x4 * 7 + x5 * 6 + x6 * 5 + x7 * 4 + x8 * 3 + x9 * 2 + x10 * 1) mod 11 == 0 12 | ``` 13 | 14 | If the result is 0, then it is a valid ISBN-10, otherwise it is invalid. 15 | 16 | ## Example 17 | 18 | Let's take the ISBN-10 `3-598-21508-8`. We plug it in to the formula, and get: 19 | ``` 20 | (3 * 10 + 5 * 9 + 9 * 8 + 8 * 7 + 2 * 6 + 1 * 5 + 5 * 4 + 0 * 3 + 8 * 2 + 8 * 1) mod 11 == 0 21 | ``` 22 | 23 | Since the result is 0, this proves that our ISBN is valid. 24 | 25 | ## Task 26 | 27 | Given a string the program should check if the provided string is a valid ISBN-10. 28 | Putting this into place requires some thinking about preprocessing/parsing of the string prior to calculating the check digit for the ISBN. 29 | 30 | The program should be able to verify ISBN-10 both with and without separating dashes. 31 | 32 | 33 | ## Caveats 34 | 35 | Converting from strings to numbers can be tricky in certain languages. 36 | Now, it's even trickier since the check digit of an ISBN-10 may be 'X' (representing '10'). For instance `3-598-21507-X` is a valid ISBN-10. 37 | 38 | ## Bonus tasks 39 | 40 | * Generate a valid ISBN-13 from the input ISBN-10 (and maybe verify it again with a derived verifier). 41 | 42 | * Generate valid ISBN, maybe even from a given starting ISBN.## Source 43 | 44 | Converting a string into a number and some basic processing utilizing a relatable real world example. [https://en.wikipedia.org/wiki/International_Standard_Book_Number#ISBN-10_check_digit_calculation](https://en.wikipedia.org/wiki/International_Standard_Book_Number#ISBN-10_check_digit_calculation) 45 | 46 | ## Submitting Incomplete Solutions 47 | It's possible to submit an incomplete solution so you can see how others have completed the exercise. 48 | -------------------------------------------------------------------------------- /run-length-encoding/test/run_length_encoding_test.clj: -------------------------------------------------------------------------------- 1 | (ns run-length-encoding-test 2 | (:require [clojure.test :refer :all] 3 | [run-length-encoding :as rle])) 4 | 5 | ;;Tests for run-length-encoding exercise 6 | 7 | (deftest encode-empty-string 8 | (testing "encode an empty string" 9 | (is (= (rle/run-length-encode "") "")))) 10 | 11 | (deftest encode-single-characters-without-count 12 | (testing "encode single characters without count" 13 | (is (= (rle/run-length-encode "XYZ") "XYZ")))) 14 | 15 | (deftest encode-string-with-no-single-characters 16 | (testing "encode string with no single characters" 17 | (is (= (rle/run-length-encode "AABBBCCCC") "2A3B4C")))) 18 | 19 | (deftest encode-string-with-single-and-mixed-characters 20 | (testing "encode string with single and mixed characters" 21 | (is (= (rle/run-length-encode "WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB") "12WB12W3B24WB")))) 22 | 23 | (deftest encode-multiple-whitespace 24 | (testing "encode string with whitespace characters mixed in it" 25 | (is (= (rle/run-length-encode " hsqq qww ") "2 hs2q q2w2 ")))) 26 | 27 | (deftest encode-lowercase 28 | (testing "encode string with lowercase characters" 29 | (is (= (rle/run-length-encode "aabbbcccc") "2a3b4c")))) 30 | 31 | (deftest decode-empty-string 32 | (testing "decode empty string" 33 | (is (= (rle/run-length-decode "") "")))) 34 | 35 | (deftest decode-single-characters 36 | (testing "decode string with single characters only" 37 | (is (= (rle/run-length-decode "XYZ") "XYZ")))) 38 | 39 | (deftest decode-no-single-characters 40 | (testing "decode string with no single characters" 41 | (is (= (rle/run-length-decode "2A3B4C") "AABBBCCCC")))) 42 | 43 | (deftest decode-single-and-repeated-characters 44 | (testing "decode string with single and repeated characters" 45 | (is (= (rle/run-length-decode "12WB12W3B24WB") "WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB")))) 46 | 47 | (deftest decode-lowercase 48 | (testing "decode string with lowercase characters" 49 | (is (= (rle/run-length-decode "2a3b4c") "aabbbcccc")))) 50 | 51 | (deftest decode-mixed-whitespace 52 | (testing "decode string with mixed whitespace characters in it" 53 | (is (= (rle/run-length-decode "2 hs2q q2w2 ") " hsqq qww ")))) 54 | 55 | (deftest consistency 56 | (testing "Encode a string and then decode it. Should return the same one." 57 | (is (= (rle/run-length-decode (rle/run-length-encode "zzz ZZ zZ")) "zzz ZZ zZ")))) 58 | -------------------------------------------------------------------------------- /space-age/test/space_age_pure_test.clj: -------------------------------------------------------------------------------- 1 | (ns space-age-pure-test 2 | (:require [space-age-pure :as sut] 3 | [clojure.test :refer [deftest is testing]])) 4 | 5 | 6 | (defn- rounds-to 7 | [expected actual] 8 | (is (= (Math/round (* 100.0 expected)) 9 | (Math/round (* 100.0 actual))))) 10 | 11 | (deftest age-on-planet-test 12 | (testing "Planet Earth" 13 | (rounds-to 31.69 (sut/age-on-planet 14 | sut/earth-relative-orbits 15 | :earth 16 | 1000000000))) 17 | 18 | (testing "Planet Mercury" 19 | (let [seconds 2134835688] 20 | (rounds-to 67.65 21 | (sut/age-on-planet sut/earth-relative-orbits :earth seconds)) 22 | (rounds-to 280.88 23 | (sut/age-on-planet sut/earth-relative-orbits :mercury seconds)))) 24 | 25 | (testing "Planet Venus" 26 | (let [seconds 189839836] 27 | (rounds-to 6.02 28 | (sut/age-on-planet sut/earth-relative-orbits :earth seconds)) 29 | (rounds-to 9.78 30 | (sut/age-on-planet sut/earth-relative-orbits :venus seconds)))) 31 | 32 | (testing "Planet Venus" 33 | (let [seconds 2329871239] 34 | (rounds-to 73.83 35 | (sut/age-on-planet sut/earth-relative-orbits :earth seconds)) 36 | (rounds-to 39.25 37 | (sut/age-on-planet sut/earth-relative-orbits :mars seconds)))) 38 | 39 | (testing "Planet Jupiter" 40 | (let [seconds 901876382] 41 | (rounds-to 28.58 42 | (sut/age-on-planet sut/earth-relative-orbits :earth seconds)) 43 | (rounds-to 2.41 44 | (sut/age-on-planet sut/earth-relative-orbits :jupiter seconds)))) 45 | 46 | (testing "Planet Saturn" 47 | (let [seconds 3000000000] 48 | (rounds-to 95.06 49 | (sut/age-on-planet sut/earth-relative-orbits :earth seconds)) 50 | (rounds-to 3.23 51 | (sut/age-on-planet sut/earth-relative-orbits :saturn seconds)))) 52 | 53 | (testing "Planet Uranus" 54 | (let [seconds 3210123456] 55 | (rounds-to 101.72 56 | (sut/age-on-planet sut/earth-relative-orbits :earth seconds)) 57 | (rounds-to 1.21 58 | (sut/age-on-planet sut/earth-relative-orbits :uranus seconds)))) 59 | 60 | (testing "Planet Neptune" 61 | (let [seconds 8210123456] 62 | (rounds-to 260.16 63 | (sut/age-on-planet sut/earth-relative-orbits :earth seconds)) 64 | (rounds-to 1.58 65 | (sut/age-on-planet sut/earth-relative-orbits :neptune seconds))))) 66 | -------------------------------------------------------------------------------- /anagram/HELP.md: -------------------------------------------------------------------------------- 1 | # Help 2 | 3 | ## Running the tests 4 | 5 | ## Clojure CLI 6 | 7 | The Clojure exercises on Exercism ship with a `deps.edn` file with a `:test` alias to invoke the [cognitect-labs test-runner](https://github.com/cognitect-labs/test-runner): 8 | 9 | ``` bash 10 | $ clj -X:test 11 | ``` 12 | 13 | ## Leiningen 14 | 15 | Leiningen can also be used to run the exercise's test by running the following command from the exercise's directory: 16 | 17 | ```bash 18 | lein test 19 | ``` 20 | 21 | ## REPL 22 | 23 | To use the REPL to run the exercise's test, run the following command from the exercise's directory: 24 | 25 | ```bash 26 | $ clj 27 | ``` 28 | 29 | -or- 30 | 31 | ```bash 32 | $ lein repl 33 | ``` 34 | 35 | Then `require` the exercise's test namespace and the Clojure test namespace): 36 | 37 | ```clojure 38 | ;; replace with the exercise's name 39 | => (require '-test) 40 | ``` 41 | 42 | Then call `run-tests` on `-test`: 43 | 44 | ```clojure 45 | ;; replace with the exercise's name 46 | => (clojure.test/run-tests '-test) 47 | ``` 48 | 49 | ## Submitting your solution 50 | 51 | You can submit your solution using the `exercism submit src/anagram.clj` command. 52 | This command will upload your solution to the Exercism website and print the solution page's URL. 53 | 54 | It's possible to submit an incomplete solution which allows you to: 55 | 56 | - See how others have completed the exercise 57 | - Request help from a mentor 58 | 59 | ## Need to get help? 60 | 61 | If you'd like help solving the exercise, check the following pages: 62 | 63 | - The [Clojure track's documentation](https://exercism.org/docs/tracks/clojure) 64 | - [Exercism's support channel on gitter](https://gitter.im/exercism/support) 65 | - The [Frequently Asked Questions](https://exercism.org/docs/using/faqs) 66 | 67 | Should those resources not suffice, you could submit your (incomplete) solution to request mentoring. 68 | 69 | To get help if you're having trouble, you can use one of the following resources: 70 | 71 | - [Ask Clojure](https://ask.clojure.org/) Official forum for Clojure Q & A. 72 | - [ClojureDocs](https://clojuredocs.org) A repository of language references and examples by function or keyword. 73 | - [/r/clojure](https://www.reddit.com/r/clojure) is the Clojure subreddit. 74 | - [StackOverflow](http://stackoverflow.com/questions/tagged/clojure) can be used to search for your problem and see if it has been answered already. You can also ask and answer questions. 75 | - [Clojureverse](https://clojureverse.org/) Friendly and inclusive Clojure(Script) Community -------------------------------------------------------------------------------- /two-fer/src/design_journal.clj: -------------------------------------------------------------------------------- 1 | (ns design-journal) 2 | 3 | ;; Talking points 4 | 5 | ;; Calling (two-fer "you") vs. returning the string directly. 6 | 7 | (reduce #(str %2 %1) "" "hello") 8 | 9 | (reduce conj '() "hello") 10 | 11 | ;; A more idiomatic and preferable approach for production code would be to use the clojure.string/join function. 12 | 13 | ;; Another approach is to solving this challenge is to use a custom function with reduce. 14 | 15 | ;; Define a function that creates a string of the two arguments it receives, swapping the order of the arguments. Use that function with reduce and two other arguments, the empty string, "" and the string to be reversed. 16 | 17 | ;; As reduce iterates over the string to be reversed, it builds up a new string in reverse order. 18 | 19 | ;; Rich comment block with redefined vars ignored 20 | #_{:clj-kondo/ignore [:redefined-var]} 21 | (comment 22 | 23 | ;; Variable arguments approaches 24 | 25 | (defn two-fer-var-arg 26 | "Using variable arguments" 27 | [& name] 28 | (if (empty? name) 29 | "One for you, one for me." 30 | (str "One for " (first name) ", one for me."))) 31 | 32 | (two-fer-var-arg) 33 | (two-fer-var-arg "Ford") 34 | 35 | (defn two-fer-var-arg-apply 36 | "Using variable arguments and apply" 37 | [& name] 38 | (if (empty? name) 39 | "One for you, one for me." 40 | (str "One for " (apply str name) ", one for me."))) 41 | 42 | (two-fer-var-arg-apply) 43 | (two-fer-var-arg-apply "Ford") 44 | 45 | (defn two-fer-var-arg-apply-when-let 46 | "Using variable arguments and apply" 47 | [& name] 48 | (let [name (or (first name) "you")] 49 | (str "One for " name ", one for me."))) 50 | 51 | (two-fer-var-arg-apply-when-let) 52 | (two-fer-var-arg-apply-when-let "Arthur")) 53 | 54 | ;; End of rich comment block 55 | 56 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 57 | ;; Alternative solutions 58 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 59 | 60 | ;; Rich comment block with redefined vars ignored 61 | #_{:clj-kondo/ignore [:redefined-var]} 62 | (comment 63 | 64 | ;; Using format and or 65 | 66 | (defn two-fer [& [name]] 67 | (format "One for %s, one for me." 68 | (or name "you")))) 69 | 70 | ;; End of rich comment block 71 | 72 | ;; Rich comment block with redefined vars ignored 73 | #_{:clj-kondo/ignore [:redefined-var]} 74 | (comment 75 | 76 | ;; Seems a little complex for the challenge 77 | ;; partial, fnil and as-> 78 | 79 | (defn two-fer [& [name]] 80 | (-> (partial format "One for %s, one for me.") 81 | (fnil "you") 82 | (as-> f (f name))))) 83 | 84 | ;; End of rich comment block 85 | -------------------------------------------------------------------------------- /hamming/src/mentor_journal.clj: -------------------------------------------------------------------------------- 1 | ;; --------------------------------------------------------- 2 | ;; Hamming Design Journal 3 | ;; 4 | ;; Exploring in the REPL to find valuable solutions 5 | ;; --------------------------------------------------------- 6 | 7 | 8 | ;; --------------------------------------------------------- 9 | (ns mentor-journal) 10 | ;; --------------------------------------------------------- 11 | 12 | ;; --------------------------------------------------------- 13 | ;; Rich comment block with redefined vars ignored 14 | (comment 15 | 16 | ;; https://exercism.io/mentor/solutions/07dd2de5c88a42fc89eae408f9d3f01e?iteration_idx=1 17 | 18 | ;; Mentor comments 19 | ;; instead of mapping the strings to a sequence of 0s and 1s and adding them, mapping = and then counting the false?s is more readable. 20 | 21 | ;; Also, I'd rather write same-size? inline, instead of as a separate function. 22 | 23 | ;; These are of course, my own subjective preferences. 24 | 25 | ;; Looks like this: 26 | 27 | ;; (defn distance [s1 s2] 28 | ;; (when (= (count s1) (count s2)) 29 | ;; (count 30 | ;; (filter false? 31 | ;; (map = s1 s2))))) 32 | 33 | ;; And to make this read more linearly (top-to-bottom), we can use the thread-last macro (->>). Looks like this: 34 | 35 | ;; (defn distance [s1 s2] 36 | ;; (when (= (count s1) (count s2)) 37 | ;; (->> (map = s1 s2) 38 | ;; (filter false?) 39 | ;; (count)))) 40 | ;; --------------------------------------------------------- 41 | 42 | ;; --------------------------------------------------------- 43 | ;; Submission 1 44 | 45 | (defn- same-size? [xs ys] 46 | (= (count xs) (count ys))) 47 | 48 | (defn- zero-one-distance [x y] 49 | (if (= x y) 0 1)) 50 | 51 | (defn distance [xs ys] 52 | (when (same-size? xs ys) 53 | (apply + (map zero-one-distance xs ys)))) 54 | ;; --------------------------------------------------------- 55 | 56 | ;; --------------------------------------------------------- 57 | ;; Submission 2 58 | 59 | (defn distance [xs ys] 60 | (when (= (count xs) (count ys)) 61 | (->> (map = xs ys) 62 | (filter false?) 63 | (count))))) 64 | 65 | ;; End of rich comment block 66 | ;; --------------------------------------------------------- 67 | 68 | ;; --------------------------------------------------------- 69 | ;; Rich comment block with redefined vars ignored 70 | (comment 71 | 72 | (ns mentor-journal) 73 | 74 | (defn distance [xs ys] 75 | (when (= (count xs) (count ys)) 76 | (->> (map = xs ys) 77 | (filter false?) 78 | (count))))) 79 | 80 | ;; End of rich comment block 81 | ;; --------------------------------------------------------- 82 | -------------------------------------------------------------------------------- /bob/src/bob.clj: -------------------------------------------------------------------------------- 1 | (ns bob 2 | (:require [clojure.string :as string])) 3 | 4 | ;; This could be the very basics of a text based adventure game or a very unintelligent chatbot 5 | 6 | ;; Bob is a lackadaisical teenager. In conversation, his responses are very limited. 7 | ;; Bob answers 'Sure.' if you ask him a question. 8 | ;; He answers 'Whoa, chill out!' if you yell at him. 9 | ;; He answers 'Calm down, I know what I'm doing!' if you yell a question at him. 10 | ;; He says 'Fine. Be that way!' if you address him without actually saying anything. 11 | ;; He answers 'Whatever.' to anything else. 12 | 13 | 14 | ;; Using re-matches and regular expression 15 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 16 | 17 | (defn response-for 18 | [phrase] 19 | (let [;; A ? at the end of the phrase, not counting whitespace 20 | question (re-matches #".*\?\s*" phrase) 21 | 22 | ;; No lower case characters, at least one upper case character 23 | yelling (re-matches #"[^a-z]*[A-Z]+[^a-z]*" phrase) 24 | 25 | ;; The entire string is whitespace 26 | silence (re-matches #"\s*" phrase)] 27 | 28 | (cond 29 | silence "Fine. Be that way!" 30 | (and question yelling) "Calm down, I know what I'm doing!" 31 | question "Sure." 32 | yelling "Whoa, chill out!" 33 | :whatever "Whatever."))) 34 | 35 | 36 | 37 | ;; Using string functions 38 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 39 | 40 | 41 | (defn response-for [phrase] 42 | (let [phrase (string/trim phrase) 43 | silence? (string/blank? phrase) 44 | question? (= \? (last phrase)) 45 | letters? (some #(Character/isLetter (int %)) phrase) 46 | shouting? (and (= phrase (string/upper-case phrase)) 47 | letters?)] 48 | (cond 49 | (and shouting? question?) "Calm down, I know what I'm doing!" 50 | silence? "Fine. Be that way!" 51 | shouting? "Whoa, chill out!" 52 | question? "Sure." 53 | :else "Whatever."))) 54 | 55 | 56 | 57 | 58 | 59 | ;; (defn- silence? [phrase] (string/blank? phrase)) 60 | 61 | ;; (defn- question? [phrase] (= \? (last phrase))) 62 | 63 | ;; (defn- has-letter? [phrase] (some #(Character/isLetter (int %)) phrase)) 64 | 65 | ;; (defn- shouting? [phrase] (and (= phrase (string/upper-case phrase)) 66 | ;; (has-letter? phrase))) 67 | 68 | ;; (defn- forceful-question? [phrase] 69 | ;; (and (shouting? phrase) (question? phrase))) 70 | 71 | ;; (defn response-for [input] 72 | ;; (let [clean (string/trim input)] 73 | ;; (cond 74 | ;; (forceful-question? clean) "Calm down, I know what I'm doing!" 75 | ;; (silence? clean) "Fine. Be that way!" 76 | ;; (shouting? clean) "Whoa, chill out!" 77 | ;; (question? clean) "Sure." 78 | ;; :else "Whatever."))) 79 | -------------------------------------------------------------------------------- /spiral-matrix/test/spiral_matrix_test.clj: -------------------------------------------------------------------------------- 1 | (ns spiral-matrix-test 2 | (:require [clojure.test :refer [deftest is testing]] 3 | [spiral-matrix :refer [spiral]])) 4 | 5 | (deftest spiral-matrix-of-0 6 | (testing "spiral matrix of 0" 7 | (is (= (spiral 0) 8 | '())))) 9 | 10 | (deftest spiral-matrix-of-1 11 | (testing "spiral matrix of 1" 12 | (is (= (spiral 1) 13 | '((1)))))) 14 | 15 | (deftest spiral-matrix-of-2 16 | (testing "spiral matrix of 2" 17 | (is (= (spiral 2) 18 | '((1 2) 19 | (4 3)))))) 20 | 21 | (deftest spiral-matrix-of-3 22 | (testing "spiral matrix of 3" 23 | (is (= (spiral 3) 24 | '((1 2 3) 25 | (8 9 4) 26 | (7 6 5)))))) 27 | 28 | (deftest spiral-matrix-of-4 29 | (testing "spiral matrix of 4" 30 | (is (= (spiral 4) 31 | '((1 2 3 4) 32 | (12 13 14 5) 33 | (11 16 15 6) 34 | (10 9 8 7)))))) 35 | 36 | (deftest spiral-matrix-of-20 37 | (testing "spiral matrix of 20" 38 | (is (= (spiral 20) 39 | '((1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20) 40 | (76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 21) 41 | (75 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 95 22) 42 | (74 143 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 161 96 23) 43 | (73 142 203 256 257 258 259 260 261 262 263 264 265 266 267 268 219 162 97 24) 44 | (72 141 202 255 300 301 302 303 304 305 306 307 308 309 310 269 220 163 98 25) 45 | (71 140 201 254 299 336 337 338 339 340 341 342 343 344 311 270 221 164 99 26) 46 | (70 139 200 253 298 335 364 365 366 367 368 369 370 345 312 271 222 165 100 27) 47 | (69 138 199 252 297 334 363 384 385 386 387 388 371 346 313 272 223 166 101 28) 48 | (68 137 198 251 296 333 362 383 396 397 398 389 372 347 314 273 224 167 102 29) 49 | (67 136 197 250 295 332 361 382 395 400 399 390 373 348 315 274 225 168 103 30) 50 | (66 135 196 249 294 331 360 381 394 393 392 391 374 349 316 275 226 169 104 31) 51 | (65 134 195 248 293 330 359 380 379 378 377 376 375 350 317 276 227 170 105 32) 52 | (64 133 194 247 292 329 358 357 356 355 354 353 352 351 318 277 228 171 106 33) 53 | (63 132 193 246 291 328 327 326 325 324 323 322 321 320 319 278 229 172 107 34) 54 | (62 131 192 245 290 289 288 287 286 285 284 283 282 281 280 279 230 173 108 35) 55 | (61 130 191 244 243 242 241 240 239 238 237 236 235 234 233 232 231 174 109 36) 56 | (60 129 190 189 188 187 186 185 184 183 182 181 180 179 178 177 176 175 110 37) 57 | (59 128 127 126 125 124 123 122 121 120 119 118 117 116 115 114 113 112 111 38) 58 | (58 57 56 55 54 53 52 51 50 49 48 47 46 45 44 43 42 41 40 39)))))) 59 | -------------------------------------------------------------------------------- /word-count/src/design_journal.clj: -------------------------------------------------------------------------------- 1 | (ns design-journal) 2 | 3 | ;; --------------------------------------------------------- 4 | (comment 5 | 6 | (require '[clojure.string :as string]) 7 | ;; Frequencies is a very useful function 8 | ;; Given a string it will return how often the characters occur 9 | (frequencies "word") 10 | ;; => {\w 1, \o 1, \r 1, \d 1} 11 | 12 | ;; Our test data is of the form 13 | ;; "one fish two fish red fish blue fish" 14 | 15 | (frequencies "one fish two fish red fish blue fish") 16 | ;; => {\space 7, \b 1, \d 1, \e 3, \f 4, \h 4, \i 4, \l 1, \n 1, \o 2, \r 1, \s 4, \t 1, \u 1, \w 1} 17 | 18 | ;; Given a collection, eg. a vector of strings 19 | ;; frequencies will return how often each word in the collection appears 20 | (frequencies ["word"]) 21 | ;; => {"word" 1} 22 | 23 | (frequencies ["one" "fish" "two" "fish" "red" "fish" "blue" "fish"]) 24 | ;; => {"one" 1, "fish" 4, "two" 1, "red" 1, "blue" 1} 25 | 26 | ;; Using frequencies we can get the shape of data required, 27 | ;; however, the string needs to be split 28 | 29 | (string/split "one fish two fish red fish blue fish" #" ") 30 | 31 | (frequencies 32 | (string/split "one fish two fish red fish blue fish" #" ")) 33 | ;; => {"one" 1, "fish" 4, "two" 1, "red" 1, "blue" 1} 34 | 35 | ;; This passes some of the tests, 36 | ;; however, when the string contains something other than words those tests fail. 37 | 38 | ;; (deftest ignore-punctuation 39 | ;; (is (= {"car" 1, "carpet" 1 "as" 1 "java" 1 "javascript" 1} 40 | ;; (word-count/word-count "car : carpet as java : javascript!!&@$%^&")))) 41 | 42 | ;; (deftest include-numbers 43 | ;; (is (= {"testing" 2 "1" 1 "2" 1} 44 | ;; (word-count/word-count "testing, 1, 2 testing")))) 45 | 46 | ;; (deftest normalize-case 47 | ;; (is (= {"go" 3} 48 | ;; (word-count/word-count "go Go GO")))) 49 | 50 | ;; Alternatively, use `re-seq` with a regular expression 51 | 52 | (re-seq #"\w+" "one fish two fish red fish blue fish") 53 | 54 | (frequencies 55 | (re-seq #"\w+" "one fish two fish red fish blue fish")) 56 | 57 | ;; Still caught by the last test, where the same word with different case 58 | ;; are counted as different words 59 | 60 | ;; (deftest normalize-case 61 | ;; (is (= {"go" 3} 62 | ;; (word-count/word-count "go Go GO")))) 63 | 64 | (frequencies 65 | (re-seq #"\w+" "go Go GO")) 66 | 67 | ;; Using lower-case then all the words would be counted as the same. 68 | 69 | (frequencies 70 | (map string/lower-case (re-seq #"\w+" "go Go GO")))) 71 | 72 | ;; End of rich comment block 73 | ;; --------------------------------------------------------- 74 | 75 | ;; --------------------------------------------------------- 76 | (comment 77 | 78 | (defn word-count [words] 79 | (->> words 80 | string/lower-case 81 | (re-seq #"\b\w+\b") 82 | frequencies))) 83 | ;; --------------------------------------------------------- 84 | -------------------------------------------------------------------------------- /bank-account/src/design_journal.clj: -------------------------------------------------------------------------------- 1 | ;; --------------------------------------------------------- 2 | ;; Software transactional memory 3 | ;; 4 | ;; The atom 5 | ;; nothing to do with fission or fusion 6 | ;; - in Clojure it is a mutable container 7 | ;; - which contains a value (which will be immutable) 8 | ;; - and the value the atom contains can be changed safely 9 | ;; --------------------------------------------------------- 10 | 11 | (ns design-journal) 12 | 13 | ;; --------------------------------------------------------- 14 | ;; Start with simple values 15 | 16 | {:name "Johnny"} 17 | 18 | (def me {:name "Johnny"}) 19 | 20 | me 21 | 22 | (assoc me :age 42) 23 | 24 | (atom {:name "Johnny"}) 25 | 26 | (def state (atom {:name "Johnny"})) 27 | 28 | state 29 | 30 | (deref state) 31 | ;; => {:name "Johnny"} 32 | 33 | @state 34 | ;; => {:name "Johnny"} 35 | 36 | ;; swap! 37 | (swap! state assoc :age 42) 38 | 39 | (deref state) 40 | ;; => {:name "Johnny", :age 42} 41 | 42 | ;; reset! 43 | (reset! state {:name "Johnny"}) 44 | ;; => {:name "Johnny"} 45 | 46 | (deref state) 47 | 48 | ;; assoc 49 | ;; assoc-in 50 | ;; update 51 | ;; update-in 52 | ;; defonce 53 | 54 | (defonce state-reloaded (atom {})) 55 | 56 | ;; Examples 57 | ;; https://practicalli.github.io/clojurescript/reagent-projects/tic-tac-toe/reagent-design-manage-app-state.html 58 | ;; https://practicalli.github.io/clojurescript/reagent-projects/tic-tac-toe/update-game-board.html 59 | ;; https://practicalli.github.io/clojurescript/reagent-projects/tic-tac-toe/refactor--empty-cell.html 60 | 61 | ;; --------------------------------------------------------- 62 | 63 | ;; --------------------------------------------------------- 64 | ;; Creating a bank account 65 | 66 | ;; Rich comment block with redefined vars ignored 67 | #_{:clj-kondo/ignore [:redefined-var]} 68 | (comment 69 | 70 | (ns bank-account) 71 | 72 | (defonce bank-accounts (atom {})) 73 | 74 | (defn open-account [] 75 | (let [account-id (java.util.UUID/randomUUID)] 76 | (swap! bank-accounts assoc account-id 0) 77 | account-id)) 78 | 79 | ;; (dissoc {:name "me"} :name) 80 | 81 | (defn close-account [account-id] 82 | (swap! bank-accounts dissoc account-id)) 83 | 84 | (defn get-balance [account-id] 85 | (get @bank-accounts account-id)) 86 | 87 | (defn update-balance [account-id credit] 88 | (swap! bank-accounts update account-id #(+ % credit)))) 89 | 90 | ;; End of rich comment block 91 | ;; --------------------------------------------------------- 92 | 93 | ;; --------------------------------------------------------- 94 | ;; Rich comment block with redefined vars ignored 95 | #_{:clj-kondo/ignore [:redefined-var]} 96 | (comment 97 | 98 | ;; Using refs 99 | 100 | (ns bank-account) 101 | 102 | (defn open-account [] 103 | (ref 0)) 104 | 105 | (defn close-account [account] 106 | (dosync 107 | (ref-set account nil))) 108 | 109 | (defn get-balance [account] 110 | (dosync 111 | @account)) 112 | 113 | (defn update-balance [account value] 114 | (dosync 115 | (alter account + value)))) 116 | 117 | ;; End of rich comment block 118 | ;; --------------------------------------------------------- 119 | -------------------------------------------------------------------------------- /bob/test/bob_test.clj: -------------------------------------------------------------------------------- 1 | (ns bob-test 2 | (:require [clojure.test :refer [deftest is]] 3 | bob)) 4 | 5 | (deftest responds-to-something 6 | (is (= "Whatever." (bob/response-for "Tom-ay-to, tom-aaaah-to.")))) 7 | 8 | (deftest responds-to-shouts 9 | (is (= "Whoa, chill out!" (bob/response-for "WATCH OUT!")))) 10 | 11 | (deftest responds-to-shouting-gibberish 12 | (is (= "Whoa, chill out!" (bob/response-for "FCECDFCAAB")))) 13 | 14 | (deftest responds-to-questions 15 | (is (= "Sure." 16 | (bob/response-for "Does this cryogenic chamber make me look fat?")))) 17 | 18 | (deftest responds-to-numeric-question 19 | (is (= "Sure." (bob/response-for "You are, what, like 15?")))) 20 | 21 | (deftest responds-to-gibberish-question 22 | (is (= "Sure." (bob/response-for "fffbbcbeab?")))) 23 | 24 | (deftest responds-to-forceful-talking 25 | (is (= "Whatever." (bob/response-for "Let's go make out behind the gym!")))) 26 | 27 | (deftest responds-to-acronyms 28 | (is (= "Whatever." 29 | (bob/response-for "It's OK if you don't want to go to the DMV.")))) 30 | 31 | (deftest responds-to-forceful-questions 32 | (is (= "Calm down, I know what I'm doing!" 33 | (bob/response-for "WHAT THE HELL WERE YOU THINKING?")))) 34 | 35 | (deftest responds-to-shouting-numbers 36 | (is (= "Whoa, chill out!" (bob/response-for "1, 2, 3 GO!")))) 37 | 38 | (deftest responds-to-no-letters 39 | (is (= "Whatever." (bob/response-for "1, 2, 3")))) 40 | 41 | (deftest responds-to-question-with-no-letters 42 | (is (= "Sure." (bob/response-for "4?")))) 43 | 44 | (deftest responds-to-shouting-with-special-characters 45 | (is (= "Whoa, chill out!" 46 | (bob/response-for "ZOMG THE %^*@#$(*^ ZOMBIES ARE COMING!!11!!1!")))) 47 | 48 | (deftest responds-to-shouting-with-no-exclamation-mark 49 | (is (= "Whoa, chill out!" (bob/response-for "I HATE THE DMV")))) 50 | 51 | (deftest responds-to-statement-containing-question-mark 52 | (is (= "Whatever." (bob/response-for "Ending with ? means a question.")))) 53 | 54 | (deftest responds-to-non-letters-with-question 55 | (is (= "Sure." (bob/response-for ":) ?")))) 56 | 57 | (deftest responds-to-prattling-on 58 | (is (= "Sure." (bob/response-for "Wait! Hang on. Are you going to be OK?")))) 59 | 60 | (deftest responds-to-silence 61 | (is (= "Fine. Be that way!" (bob/response-for "")))) 62 | 63 | (deftest responds-to-prolonged-silence 64 | (is (= "Fine. Be that way!" (bob/response-for " ")))) 65 | 66 | (deftest responds-to-alternate-silence 67 | (is (= "Fine. Be that way!" (bob/response-for "\t\t\t\t\t\t\t\t\t\t")))) 68 | 69 | (deftest responds-to-multiple-line-question 70 | (is (= "Whatever." 71 | (bob/response-for "\nDoes this cryogenic chamber make me look fat?\nNo.")))) 72 | 73 | (deftest responds-to-starting-with-whitespace 74 | (is (= "Whatever." (bob/response-for " hmmmmmmm...")))) 75 | 76 | (deftest responds-to-ending-with-whitespace 77 | (is (= "Sure." (bob/response-for "Okay if like my spacebar quite a bit? ")))) 78 | 79 | (deftest responds-to-other-whitespace 80 | (is (= "Fine. Be that way!" (bob/response-for "\n\r \t")))) 81 | 82 | (deftest responds-to-non-question-ending-with-whitespace 83 | (is (= "Whatever." 84 | (bob/response-for "This is a statement ending with whitespace ")))) 85 | -------------------------------------------------------------------------------- /space-age/src/space_age_pure.clj: -------------------------------------------------------------------------------- 1 | (ns space-age-pure) 2 | 3 | ;; A pure function approach to the solution 4 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 5 | 6 | ;; Helpers 7 | (def seconds->day 8 | (let [hours 24 minutes 60 seconds 60] 9 | (* hours minutes seconds))) 10 | 11 | 12 | (def earth-relative-orbits 13 | "Orbital periods for planets in the sol system, 14 | relative to the orbit of Earth" 15 | {:earth 365.25 16 | :jupiter 11.862615 17 | :mars 1.8808158 18 | :mercury 0.2408467 19 | :neptune 164.79132 20 | :saturn 29.447498 21 | :uranus 84.016846 22 | :venus 0.61519726}) 23 | 24 | (defn age-on-planet 25 | [relative-orbits planet duration] 26 | (if (= planet :earth) 27 | (/ duration (:earth relative-orbits) seconds->day) 28 | (/ duration 29 | (* (:earth relative-orbits) (planet relative-orbits)) 30 | seconds->day))) 31 | 32 | 33 | #_(def relative-orbits 34 | "Orbital periods for planets in the sol system, 35 | relative to the orbit of Earth" 36 | {:earth 3.15576E7 37 | :jupiter 3.7435565912399995E8 38 | :mars 5.9354032690079994E7 39 | :mercury 7600543.81992 40 | :neptune 5.200418560032E9 41 | :saturn 9.292923628848001E8 42 | :uranus 2.6513700193296E9 43 | :venus 1.9414149052176E7 }) 44 | 45 | #_(defn age-on-planet 46 | [relative-orbits planet duration] 47 | (/ duration (planet relative-orbits))) 48 | 49 | 50 | 51 | (eval 52 | `(do 53 | ~@(for [[planet mult] multipliers] 54 | `(defn ~(symbol (str "on-" (name planet))) 55 | ~(str "Returns age in years on " (name planet) " given age in seconds.") [~'a] 56 | (/ ~'a ~(* (+ 365 1/4) 24 60 60 mult)))))) 57 | 58 | 59 | ;; Another evil macro version 60 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 61 | 62 | (def earth-years {:mercury 0.2408467 63 | :venus 0.61519726 64 | :earth 1.0 65 | :mars 1.8808158 66 | :jupiter 11.862615 67 | :saturn 29.447498 68 | :uranus 84.016846 69 | :neptune 164.79132}) 70 | 71 | (defn convert-fn-template [planet] 72 | `(def ~(symbol (str "on-" (name planet))) 73 | (fn [~'sec] 74 | (-> ~'sec 75 | (#(/ % 31557600)) ; sec -> earth years 76 | (#(/ % (earth-years ~planet))))))) ; earth years -> planet years 77 | 78 | ; generate conversion functions for each planet 79 | (eval `(do ~@(map convert-fn-template (keys earth-years)))) 80 | 81 | 82 | ;; Very nice higher order function 83 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 84 | 85 | 86 | (defn- seconds-to-earth-years [seconds] 87 | (/ seconds 31557600.0)) 88 | 89 | (defn- age-fn [year-ratio] 90 | (fn [seconds] 91 | (let [earth-years (seconds-to-earth-years seconds)] 92 | (/ earth-years year-ratio)))) 93 | 94 | (def on-mercury (age-fn 0.240846)) 95 | (def on-venus (age-fn 0.6151972)) 96 | (def on-earth (age-fn 1)) 97 | (def on-mars (age-fn 1.8808158)) 98 | (def on-jupiter (age-fn 11.862615)) 99 | (def on-saturn (age-fn 29.447498)) 100 | (def on-neptune (age-fn 164.79132)) 101 | (def on-uranus (age-fn 84.016846)) 102 | -------------------------------------------------------------------------------- /pov/test/pov_test.clj: -------------------------------------------------------------------------------- 1 | (ns pov-test 2 | (:require [clojure.test :refer [deftest is]] 3 | pov)) 4 | 5 | ;;; Inputs. 6 | 7 | (def singleton [:x]) 8 | 9 | (def simple-tree [:parent [:sibling] [:x]]) 10 | 11 | (def large-flat 12 | [:parent [:sib-a] 13 | [:sib-b] 14 | [:x] 15 | [:sib-c] 16 | [:sib-d]]) 17 | 18 | (def deeply-nested 19 | [:level-0 20 | [:level-1 21 | [:level-2 22 | [:level-3 23 | [:level-4 24 | [:x]]]]]]) 25 | 26 | (def cousins 27 | [:grand-parent 28 | [:parent 29 | [:sib-1] 30 | [:x] 31 | [:sib-2]] 32 | [:uncle 33 | [:cousin-1] 34 | [:cousin-2]]]) 35 | 36 | (def target-with-children 37 | [:grand-parent 38 | [:parent 39 | [:x 40 | [:child-1] 41 | [:child-2]] 42 | [:sibling 43 | [:nephew] 44 | [:niece]]] 45 | [:aunt 46 | [:cousin-1 47 | [:2nd-cousin-1] 48 | [:2nd-cousin-2]] 49 | [:cousin-2 50 | [:2nd-cousin-3] 51 | [:2nd-cousin-4]]]]) 52 | 53 | ;;; Expected results. 54 | 55 | (def simple-pulled [:x [:parent [:sibling]]]) 56 | 57 | (def flat-pulled 58 | [:x [:parent 59 | [:sib-a] 60 | [:sib-b] 61 | [:sib-c] 62 | [:sib-d]]]) 63 | 64 | (def nested-pulled 65 | [:x 66 | [:level-4 67 | [:level-3 68 | [:level-2 69 | [:level-1 70 | [:level-0]]]]]]) 71 | 72 | (def cousins-pulled 73 | [:x 74 | [:parent 75 | [:sib-1] 76 | [:sib-2] 77 | [:grand-parent 78 | [:uncle 79 | [:cousin-1] 80 | [:cousin-2]]]]]) 81 | 82 | (def with-kids-pulled 83 | [:x 84 | [:child-1] 85 | [:child-2] 86 | [:parent 87 | [:sibling 88 | [:nephew] 89 | [:niece]] 90 | [:grand-parent 91 | [:aunt 92 | [:cousin-1 93 | [:2nd-cousin-1] 94 | [:2nd-cousin-2]] 95 | [:cousin-2 96 | [:2nd-cousin-3] 97 | [:2nd-cousin-4]]]]]]) 98 | 99 | (deftest test-pov 100 | (is (= singleton 101 | (pov/of :x singleton)) 102 | "Can handle singletons") 103 | (is (= simple-pulled 104 | (pov/of :x simple-tree)) 105 | "Can handle simple trees") 106 | (is (= nested-pulled 107 | (pov/of :x deeply-nested)) 108 | "Can handle nested trees") 109 | (is (= flat-pulled 110 | (pov/of :x large-flat)) 111 | "Can extract a node from a list of siblings") 112 | (is (= cousins-pulled 113 | (pov/of :x cousins)) 114 | "Can handle moderate trees") 115 | (is (= with-kids-pulled 116 | (pov/of :x target-with-children)) 117 | "Can handle complex trees")) 118 | 119 | (deftest not-found 120 | (is (nil? (pov/of :not-found! target-with-children)) 121 | "Returns nil if we cannot reparent") 122 | (is (nil? (pov/of :x [])) 123 | "Return nil if the input is empty") 124 | (is (nil? (pov/of :x nil)) 125 | "Returns nil if the input is nil")) 126 | 127 | (deftest path-from-to 128 | (is (= [:x :parent] 129 | (pov/path-from-to :x :parent simple-tree)) 130 | "Can trace a path from target to parent") 131 | (is (= [:x :parent :sib-c] 132 | (pov/path-from-to :x :sib-c large-flat)) 133 | "Can trace a path from target to sibling") 134 | (is (= [:x :parent :grand-parent :aunt :cousin-1 :2nd-cousin-1] 135 | (pov/path-from-to :x :2nd-cousin-1 target-with-children)) 136 | "Can trace a path from :x to :2nd-cousin-1") 137 | (is (nil? (pov/path-from-to :x :not-there! cousins)) 138 | "Returns nil if there is no path")) 139 | -------------------------------------------------------------------------------- /collatz-conjecture/src/design_journal.clj: -------------------------------------------------------------------------------- 1 | ;; --------------------------------------------------------- 2 | ;; Exercisim: Collatz Conjecture 3 | ;; 4 | ;; Design journal showing evolution of solution 5 | ;; --------------------------------------------------------- 6 | 7 | 8 | ;; --------------------------------------------------------- 9 | (ns design-journal) 10 | ;; --------------------------------------------------------- 11 | 12 | ;; --------------------------------------------------------- 13 | ;; Rich comment block with redefined vars ignored 14 | #_{:clj-kondo/ignore [:redefined-var]} 15 | (comment 16 | 17 | ;; Using a recursive function 18 | ;; with larger values this could blow up the stack 19 | 20 | (ns collatz-conjecture) 21 | 22 | (defn collatz [num] 23 | (cond 24 | (= 1 num) 0 25 | (even? num) (+ 1 (collatz (/ num 2))) 26 | :else (+ 1 (collatz (+ (* 3 num) 1))))) 27 | 28 | (collatz 1)) 29 | 30 | ;; refine the above using iterate and take-while 31 | ;; implement it with functions like iterate and take-while as an alternative 32 | ;; num) 33 | ;; (iterate 34 | ;; #(if(even? %) 35 | ;; (/ % 2) 36 | ;; (+(* % 3) 1)) num))) 37 | 38 | ;; (collatz-improved 12) 39 | 40 | ;; I'm not getting how i can pass the current number of the iteration to the predicate function of the take-while. 41 | 42 | ;; You can do something like: 43 | 44 | ;; (->> num 45 | ;; (iterate the-fn) 46 | ;; (take-while bigger....) 47 | ;; ....) 48 | 49 | ;; The function the-fn will implement the if etc... 50 | 51 | ;; End of rich comment block 52 | ;; --------------------------------------------------------- 53 | 54 | ;; --------------------------------------------------------- 55 | ;; Rich comment block with redefined vars ignored 56 | #_{:clj-kondo/ignore [:redefined-var]} 57 | (comment 58 | 59 | ;; comments: 60 | 61 | ;; To check for a condition you can use assert. No need to use if. 62 | ;; I would avoid using variable names that shadow functions like count. 63 | ;; Well done using recur to call the function recursively. 64 | ;; Having said that is always a good idea to check for existing functions that provide the desired functionality. 65 | ;; Please take a look at iterate and take-while. 66 | ;; You could extract the if to check if the number is even? to an auxiliary function. 67 | 68 | (defn collatz 69 | "Given a starting num, returns the count of steps needed for the Collatz Conjecture to reach 1" 70 | ([num] 71 | (if (or (not (number? num)) 72 | (< num 1)) 73 | (throw (AssertionError. "num should be a positive number")) 74 | (collatz 0))) 75 | ([num count] 76 | (if (= num 1) 77 | count 78 | (recur (if (even? num) 79 | (/ num 2) 80 | (inc (* num 3))) 81 | (inc count))))) 82 | 83 | ;; Refactor 84 | 85 | (defn collatz-step [num] 86 | (if (even? num) 87 | (/ num 2) 88 | (inc (* num 3)))) 89 | 90 | (defn collatz 91 | "Given a starting num, returns the count of steps needed for the Collatz Conjecture to reach 1" 92 | ([num] 93 | (assert (and (number? num) 94 | (> num 0)) 95 | "num should be a positive number") 96 | (->> num 97 | (iterate collatz-step) 98 | (take-while #(not= % 1)) 99 | count)))) 100 | ;; End of rich comment block 101 | ;; --------------------------------------------------------- 102 | -------------------------------------------------------------------------------- /hamming/src/design_journal.clj: -------------------------------------------------------------------------------- 1 | ;; --------------------------------------------------------- 2 | ;; Hamming design journal 3 | ;; 4 | ;; REPL experiements to discover valuable solutions 5 | ;; --------------------------------------------------------- 6 | 7 | ;; --------------------------------------------------------- 8 | (ns design-journal) 9 | ;; --------------------------------------------------------- 10 | 11 | ;; --------------------------------------------------------- 12 | ;; First thought is to use an anonymous function with filter 13 | 14 | #_(defn distance [strand1 strand2] ; <- arglist goes here 15 | ;; your code goes here 16 | (filter #(= %1 %2) strand1 strand2)) 17 | 18 | ;; unfortunately filter only works over one data structure. 19 | 20 | ;; clojure.core/compare simple values and collections 21 | ;; https://clojuredocs.org/clojure.core/compare 22 | 23 | #_(defn distance [strand1 strand2] ; <- arglist goes here 24 | ;; your code goes here 25 | (compare strand1 strand2)) 26 | 27 | ;; Solves a few tests, but not exactly the solution we are looking for. 28 | ;; A very useful function to be aware of though. 29 | 30 | ;; If the values are not the same lenght, return nil 31 | ;; this can be done with a simple if expression. 32 | 33 | #_(defn distance [strand1 strand2] ; <- arglist goes here 34 | ;; your code goes here 35 | (if (= (count strand1) (count strand2)) 36 | (compare strand1 strand2) 37 | nil)) 38 | 39 | ;; using map we can iterate over multiple functions 40 | ;; starting with just a simple compare condition 41 | 42 | (map (fn [a b] 43 | (if (= a b) 44 | 0 45 | 1)) 46 | "GGACTGA" "GGACTGA") 47 | 48 | ;; with different strands 49 | 50 | (map (fn [a b] 51 | (if (= a b) 52 | 0 53 | 1)) 54 | "GCACTGA" "GGACTGA") 55 | 56 | ;; now we can just add up the numbers to get the hammer value 57 | 58 | (apply + 59 | (map (fn [a b] 60 | (if (= a b) 61 | 0 62 | 1)) 63 | "GCACTGA" "GGACTGA")) 64 | 65 | (defn distance [strand1 strand2] 66 | ;; your code goes here 67 | (if (= (count strand1) (count strand2)) 68 | (apply + 69 | (map (fn [a b] 70 | (if (= a b) 71 | 0 72 | 1)) 73 | strand1 strand2)) 74 | nil)) 75 | 76 | ;; refactor using the function definition syntax short form 77 | 78 | (defn distance [strand1 strand2] 79 | ;; your code goes here 80 | (if (= (count strand1) (count strand2)) 81 | (apply + 82 | (map #(if (= %1 %2) 0 1) 83 | strand1 strand2)) 84 | nil)) 85 | 86 | ;; refactor to a when rather than if, 87 | ;; as only one branch required. 88 | 89 | ;; if returns nil if a second expression is not defined 90 | ;; and the condition is false 91 | (if (= 1 2) 92 | "not equal") 93 | 94 | #_{:clj-kondo/ignore [:redefined-var]} 95 | (defn distance [strand1 strand2] 96 | ;; your code goes here 97 | (when (= (count strand1) (count strand2)) 98 | (apply + (map #(if (= % %2) 0 1) strand1 strand2)))) 99 | 100 | ;; a slightly more abstract approach 101 | ;; map not equals over the two strands 102 | 103 | (map not= "ABC" "AOC") 104 | 105 | ;; filter the result with identity 106 | 107 | (filter identity (map not= "ABC" "AOC")) 108 | 109 | ;; then count how many elements were returned 110 | ;; and that is the number of characters that are different 111 | 112 | (count 113 | (filter identity (map not= "ABC" "AOC"))) 114 | 115 | (defn distance [strand1 strand2] 116 | (when (= (count strand1) (count strand2)) 117 | (count (filter identity (map not= strand1 strand2))))) 118 | 119 | ;; not a huge difference in terms of size, 120 | ;; but feels more generic 121 | 122 | ;; Other uses for identity 123 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 124 | 125 | ;; flattening a nested structure 126 | (mapcat identity [[[0 1] [1 2]] [[11 12]]]) 127 | ;; => ([0 1] [1 2] [11 12]) 128 | 129 | (identity []) 130 | 131 | ;; replacement for `or` and `and` 132 | ;; they are both macros, so cannot be used as an argument function 133 | 134 | (and true true) 135 | (apply and '(true false true true)) 136 | 137 | ;; and 138 | 139 | #_(apply and [true 1 "yes"]) 140 | 141 | (every? identity [true 1 "yes"]) 142 | 143 | (every? identity [true nil "yes"]) 144 | -------------------------------------------------------------------------------- /hamming/src/hamming.clj: -------------------------------------------------------------------------------- 1 | (ns hamming) 2 | 3 | ;; --------------------------------------------------------- 4 | ;; First thought is to use an anonymous function with filter 5 | 6 | #_(defn distance [strand1 strand2] 7 | ;; your code goes here 8 | (filter #(= %1 %2) strand1 strand2)) 9 | 10 | ;; unfortunately filter only works over one data structure. 11 | 12 | ;; clojure.core/compare simple values and collections 13 | ;; https://clojuredocs.org/clojure.core/compare 14 | 15 | #_(defn distance [strand1 strand2] ; <- arglist goes here 16 | ;; your code goes here 17 | (compare strand1 strand2)) 18 | 19 | ;; Solves a few tests, but not exactly the solution we are looking for. 20 | ;; A very useful function to be aware of though. 21 | 22 | ;; If the values are not the same lenght, return nil 23 | ;; this can be done with a simple if expression. 24 | 25 | #_(defn distance [strand1 strand2] ; <- arglist goes here 26 | ;; your code goes here 27 | (if (= (count strand1) (count strand2)) 28 | (compare strand1 strand2) 29 | nil)) 30 | 31 | ;; using map we can iterate over multiple functions 32 | ;; starting with just a simple compare condition 33 | 34 | (map (fn [a b] 35 | (if (= a b) 36 | 0 37 | 1)) 38 | "GGACTGA" "GGACTGA") 39 | 40 | ;; with different strands 41 | 42 | (map (fn [a b] 43 | (if (= a b) 44 | 0 45 | 1)) 46 | "GCACTGA" "GGACTGA") 47 | 48 | ;; now we can just add up the numbers to get the hammer value 49 | 50 | (apply + 51 | (map (fn [a b] 52 | (if (= a b) 53 | 0 54 | 1)) 55 | "GCACTGA" "GGACTGA")) 56 | 57 | (defn distance [strand1 strand2] 58 | ;; your code goes here 59 | (if (= (count strand1) (count strand2)) 60 | (apply + 61 | (map (fn [a b] 62 | (if (= a b) 63 | 0 64 | 1)) 65 | strand1 strand2)) 66 | nil)) 67 | 68 | ;; refactor using the function definition syntax short form 69 | 70 | (defn distance [strand1 strand2] 71 | ;; your code goes here 72 | (if (= (count strand1) (count strand2)) 73 | (apply + 74 | (map #(if (= %1 %2) 0 1) 75 | strand1 strand2)) 76 | nil)) 77 | 78 | ;; refactor to a when rather than if, 79 | ;; as only one branch required. 80 | 81 | ;; if returns nil if a second expression is not defined 82 | ;; and the condition is false 83 | (if (= 1 2) 84 | "not equal") 85 | 86 | (defn distance [strand1 strand2] 87 | ;; your code goes here 88 | (when (= (count strand1) (count strand2)) 89 | (apply + (map #(if (= % %2) 0 1) strand1 strand2)))) 90 | 91 | ;; a slightly more abstract approach 92 | ;; map not equals over the two strands 93 | 94 | (map not= "ABC" "AOC") 95 | 96 | ;; filter the result with identity 97 | 98 | (filter identity (map not= "ABC" "AOC")) 99 | 100 | ;; then count how many elements were returned 101 | ;; and that is the number of characters that are different 102 | 103 | (count 104 | (filter identity (map not= "ABC" "AOC"))) 105 | 106 | ;; --------------------------------------------------------- 107 | 108 | ;; --------------------------------------------------------- 109 | 110 | (defn distance [strand1 strand2] 111 | (when (= (count strand1) (count strand2)) 112 | (count (filter identity (map not= strand1 strand2))))) 113 | 114 | ;; not a huge difference in terms of size, 115 | ;; but feels more generic 116 | 117 | ;; Other uses for identity 118 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 119 | 120 | ;; flattening a nested structure 121 | (mapcat identity [[[0 1] [1 2]] [[11 12]]]) 122 | ;; => ([0 1] [1 2] [11 12]) 123 | 124 | (identity []) 125 | 126 | ;; replacement for `or` and `and` 127 | ;; they are both macros, so cannot be used as an argument function 128 | 129 | (and true true) 130 | (apply and '(true false true true)) 131 | 132 | ;; and 133 | 134 | #_(apply and [true 1 "yes"]) 135 | 136 | (every? identity [true 1 "yes"]) 137 | 138 | (every? identity [true nil "yes"]) 139 | 140 | ;; not a huge difference in terms of size, 141 | ;; but feels more generic 142 | ;; --------------------------------------------------------- 143 | 144 | ;; --------------------------------------------------------- 145 | ;; Other uses for identity 146 | 147 | ;; flattening a nested structure 148 | (mapcat identity [[[0 1] [1 2]] [[11 12]]]) 149 | ;; => ([0 1] [1 2] [11 12]) 150 | 151 | (identity []) 152 | 153 | ;; replacement for `or` and `and` 154 | ;; they are both macros, so cannot be used as an argument function 155 | 156 | (and true true) 157 | (apply and '(true false true true)) 158 | 159 | ;; and 160 | 161 | #_(apply and [true 1 "yes"]) 162 | 163 | (every? identity [true 1 "yes"]) 164 | 165 | (every? identity [true nil "yes"]) 166 | 167 | (defn comp 168 | [s1 s2] 169 | (if (not= s1 s2) 170 | 1 171 | 0)) 172 | 173 | (defn distance 174 | [strand1 strand2] 175 | (when (= (count strand1) (count strand2)) 176 | (reduce + (map comp strand1 strand2)))) 177 | ;; --------------------------------------------------------- 178 | -------------------------------------------------------------------------------- /rna-transcription/src/design_journal_redux.clj: -------------------------------------------------------------------------------- 1 | (ns design-journal-redux 2 | (:require clojure.string)) 3 | 4 | 5 | ;; Mapping between DNA to RNA 6 | ;; A state transaction or dictionary 7 | 8 | ;; - `G` -> `C` 9 | ;; - `C` -> `G` 10 | ;; - `T` -> `A` 11 | ;; - `A` -> `U` 12 | 13 | ;; Starting function definition 14 | 15 | (def dna->rna 16 | "DNA to RNA transcription mappings" 17 | {\G \C \C \G \T \A \A \U}) 18 | 19 | 20 | (defn to-rna 21 | "Transcribe each nucleotide from a DNA strand into its RNA complement 22 | Arguments: string representing DNA strand 23 | Return: string representing RNA strand" 24 | [dna] 25 | (clojure.string/join 26 | (map #(or (dna->rna %) 27 | (throw (AssertionError. "Unknown nucleotide"))) 28 | dna))) 29 | 30 | (comment 31 | (to-rna "GATTAG")) 32 | 33 | 34 | ;; Annotation: Ignore lint rules 35 | #_{:clj-kondo/ignore [:redefined-var :clojure-lsp/unused-public-var]} 36 | (comment 37 | (str "coding experiments") 38 | 39 | 40 | ;; How to represent the data? 41 | 42 | (def dna-dictionary 43 | "Convert DNA to RNA" 44 | {"G" "C" 45 | "C" "G" 46 | "T" "A" 47 | "A" "U"}) 48 | 49 | 50 | ;; Write a function to get the value for a given key (dna nucleotide) 51 | 52 | (defn to-rna 53 | "Convert a specific nucleotide from a DNA strand, 54 | into a nucleotide for an RNA strand" 55 | [nucleotide] 56 | (get dna-dictionary nucleotide)) 57 | 58 | ;; (to-rna "G") 59 | 60 | ;; Refactor 61 | 62 | ;; Extract the funtion it its own definition 63 | ;; Pass a dictionary to make the function pure / deterministic 64 | 65 | (defn dna->rna 66 | "Convert a specific nucleotide from a DNA strand, 67 | into a nucleotide for an RNA strand" 68 | [dictionary nucleotide] 69 | (get dictionary nucleotide)) 70 | 71 | (dna->rna dna-dictionary "C") 72 | 73 | 74 | ;; Iterate over the whole string 75 | 76 | ;; Example DNA sequence 77 | "ACGTGGTCTTAA" 78 | 79 | (map 80 | (fn [character] (clojure.string/lower-case character)) 81 | "ACGTGGTCTTAA") 82 | ;;("a" "c" "g" "t" "g" "g" "t" "c" "t" "t" "a" "a") 83 | 84 | ;; Now for DNA to RNA 85 | 86 | (map 87 | (fn [nucleotide] (dna->rna dna-dictionary nucleotide)) 88 | "ACGTGGTCTTAA") 89 | ;;("U" "G" "C" "A" "C" "C" "A" "G" "A" "A" "U" "U") 90 | 91 | ;; Nil nil nil , oh dear 92 | 93 | 94 | ;; Investigation 95 | ;; - what value is returned from the expressions above? 96 | 97 | ;; Refactor 98 | 99 | (defn dna->rna 100 | "Convert a specific nucleotide from a DNA strand, 101 | into a nucleotide for an RNA strand" 102 | [dictionary nucleotide] 103 | (get dictionary (str nucleotide))) 104 | 105 | 106 | ;; Is the returned data correct though? 107 | 108 | ;; Update the map expression to return the correct form of result 109 | 110 | (clojure.string/join '("U" "G" "C" "A" "C" "C" "A" "G" "A" "A" "U" "U")) 111 | 112 | ;; (quote ("U" "G" "C" "A" "C" "C" "A" "G" "A" "A" "U" "U")) 113 | 114 | ;; Refactor 115 | ;; Define the `to-rna` funciton that iterates over the DNA sequence 116 | ;; Return a single sequence 117 | 118 | 119 | (defn to-rna 120 | "Arguments: string representing DNA strand 121 | Return: string representing RNA strand" 122 | [dna] 123 | (clojure.string/join 124 | (map #(dna->rna dna-dictionary %) 125 | dna)) 126 | 127 | ;; (fn [nucleotide] (dna->rna dna-dictionary nucleotide)) 128 | 129 | 130 | ;; Use the test example 131 | (to-rna "ACGTGGTCTTAA") 132 | 133 | ;; One more test case 134 | ;; Assertion Error for "XCGFGGTDTTAA" 135 | 136 | ;; throw (AssertionError. "message") 137 | 138 | ;; take our test code 139 | 140 | (map #(dna->rna dna-dictionary %) 141 | "XCGFGGTDTTAA")) 142 | 143 | ;;(nil "G" "C" nil "C" "C" "A" nil "A" "A" "U" "U") 144 | 145 | ;; Using the underlying error mechinisim isnt strictly neccessary 146 | ;; but it is part of the unit test 147 | 148 | ;; Accessing a map returns a falsy value that can be used as a condition 149 | 150 | ;; `or` macro will run the first expression, 151 | ;; if a false value is returned, or runs the next expression 152 | 153 | 154 | (or convert-dna 155 | (throw (new AssertionError "Unknown nucleotide"))) 156 | ;; Assemble experiments into the final code and run tests... 157 | #_()) ; End of rich comment 158 | 159 | ;; --------------------------------------------------------- 160 | ;; Final Solution 161 | 162 | ;; (def dna->rna 163 | ;; "DNA to RNA transcription mappings" 164 | ;; {\G \C \C \G \T \A \A \U}) 165 | ;; 166 | ;; 167 | ;; (defn to-rna 168 | ;; "Transcribe each nucleotide from a DNA strand into its RNA complement 169 | ;; Arguments: string representing DNA strand 170 | ;; Return: string representing RNA strand" 171 | ;; [dna] 172 | ;; (clojure.string/join 173 | ;; (map #(or (dna->rna %) 174 | ;; (throw (AssertionError. "Unknown nucleotide"))) 175 | ;; dna))) 176 | 177 | ;; NOTE: a map can be used as a function, with a key given as an argument 178 | 179 | ;; NOTE: the anonymous function has a short form 180 | 181 | ;; Normal form 182 | (fn [argument1 argument2] 183 | (str argument1 argument2)) 184 | 185 | ;; Short form 186 | #(str % %2) 187 | -------------------------------------------------------------------------------- /spiral-matrix/src/spiral_matrix.clj: -------------------------------------------------------------------------------- 1 | (ns spiral-matrix) 2 | 3 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 4 | ;; Taking a lateral thinking approach 5 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 6 | 7 | ;; For a 3x3 matrix we have the following rows. 8 | ;; 1 2 3 9 | ;; 4 5 6 10 | ;; 7 8 9 11 | 12 | ;; The spiral version of the matrix is 13 | ;; 1 2 3 14 | ;; 8 9 4 15 | ;; 7 6 5 16 | 17 | ;; Which gives a sequence of 18 | ;; [1 2 3 8 9 4 7 6 5] 19 | 20 | ;; Looking at the relative positions of the numbers 21 | 22 | ;; 1 goes into position 1 23 | ;; 2 goes into position 2 24 | ;; 3 goes into position 3 25 | ;; 4 goes into position 6 26 | ;; 5 goes into position 9 27 | ;; 6 goes into position 8 28 | ;; 7 goes into position 6 29 | ;; 8 goes into position 3 30 | ;; 9 goes into position 4 31 | 32 | ;; So if we can generate the following sequence 33 | ;; 1 2 3 6 9 8 6 3 4 34 | 35 | ;; then we can map them to incremental numbers in the matrix 36 | ;; and get an indexed set of values that can generate the spiral from. 37 | 38 | ;; 1 2 3 6 9 8 6 3 4 ;; location on grid 39 | ;; 1 2 3 4 5 6 7 8 9 ;; value at location 40 | 41 | 42 | (defn spiral [n] 43 | (let [progression (cycle [1 n -1 (- n)])] 44 | (->> (range (dec n) 0 -1) 45 | (mapcat #(repeat 2 %)) 46 | (cons n) 47 | (mapcat #(repeat %2 %) progression) 48 | (reductions +) 49 | (map vector (range 1 (inc (* n n)))) 50 | (sort-by second) 51 | (map first) 52 | (partition n)))) 53 | 54 | 55 | 56 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 57 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 58 | 59 | ;; Rich comment block with redefined vars ignored 60 | #_{:clj-kondo/ignore [:redefined-var]} 61 | (comment 62 | 63 | ;; Very simplistic approach 64 | 65 | ;; Passing first two tests 66 | ;; `spiral-matrix-of-0` and `spiral-matrix-of-1` 67 | #_(defn spiral [size] 68 | (let [sequence (partition size (take (* size size) (rest (range))))] 69 | sequence) 70 | ) 71 | 72 | 73 | ;; Passes test `spiral-matrix-of-2` 74 | ;; Fails all other tests 75 | #_(defn spiral 76 | [size] 77 | (let [sequence (partition size (take (* size size) (rest (range))))] 78 | (list 79 | (first sequence) 80 | (reverse (second sequence))))) 81 | 82 | 83 | 84 | ) ;; End of rich comment block 85 | 86 | 87 | ;; Rich comment block with redefined vars ignored 88 | #_{:clj-kondo/ignore [:redefined-var]} 89 | (comment 90 | 91 | ;; Recursive function approach 92 | 93 | (defn rotate [matrix] 94 | (apply map list (reverse matrix))) 95 | 96 | (defn spiral-matrix [size new-size start] 97 | (let [row (list (range start (+ start size)))] 98 | (if (= 1 new-size) 99 | row 100 | (concat row 101 | (rotate (spiral-matrix (dec new-size ) size (+ start size))))))) 102 | 103 | (defn spiral [size] 104 | (if (> size 0) 105 | (spiral-matrix size size 1) 106 | '())) 107 | 108 | 109 | ) ;; End of rich comment block 110 | 111 | 112 | 113 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 114 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 115 | 116 | 117 | ;; Rich comment block with redefined vars ignored 118 | #_{:clj-kondo/ignore [:redefined-var]} 119 | (comment 120 | 121 | ;; Very manual approach 122 | 123 | (ns spiral-matrix 124 | (:require [clojure.pprint :refer [pprint]])) 125 | 126 | 127 | ;; strategy: 128 | ;; Example: spriral 5 129 | ;; 5-to-right, 1 2 3 4 5 130 | ;; 4-down 6 7 8 9, 131 | ;; 4-left 10 11 12 13, 132 | ;; 3-up 14 15 16, 133 | ;; 3-right 17 18 19 134 | ;; 2-d 20 21, 135 | ;; 2-l 22 23 136 | ;; 1-u 24, 137 | ;; 1r 25 138 | ;; general: right(n) down(n-1) left(n-1) up(n-2) start again --> right(n-2) 139 | ;; Eg. 3 --> 3r (123) 2d (45) 2l (67) 1u (8) 1r (9) 140 | (defn spiral [n] 141 | (let [segments (as-> (range (dec n) 0 -1) col 142 | (map #(vector % %) col) 143 | (flatten (conj col n))) 144 | 145 | directions (->> (repeat '(1 0 0 1 -1 0 0 -1)) 146 | (take (Math/ceil (/ (count segments) 4))) 147 | flatten 148 | (partition 2)) 149 | 150 | val-partitions (drop 1 151 | (reduce 152 | #(conj %1 153 | (range 154 | (inc (last (last %1))) 155 | (inc (+ (last (last %1)) %2)))) 156 | [[0]] segments)) 157 | 158 | steps (partition 3 (flatten 159 | (map (fn [d s] 160 | (flatten 161 | (map (fn [v] [d v]) s))) 162 | directions val-partitions))) 163 | 164 | x (atom -1) 165 | 166 | y (atom 0) 167 | 168 | matrix (atom (vec (repeat n (vec (repeat n 0))))) 169 | 170 | matrix (reduce #(do 171 | (swap! x + (first %2)) 172 | (swap! y + (second %2)) 173 | (reset! %1 (assoc-in @%1 [@y @x] (last %2))) 174 | %1) 175 | matrix steps)] 176 | @matrix)) 177 | 178 | ) ;; End of rich comment block 179 | -------------------------------------------------------------------------------- /nucleotide-count/src/design_journal.clj: -------------------------------------------------------------------------------- 1 | ;; (ns nucleotide-count) 2 | (ns design-journal) 3 | 4 | ;; Given a single stranded DNA string, compute how many times each nucleotide occurs in the string. 5 | 6 | (def valid-nucleotides 7 | "Characters representing valid nucleotides" 8 | [\A \C \G \T]) 9 | 10 | ;; Function definition signatures (derived from test code) 11 | 12 | (defn count-of-nucleotide-in-strand 13 | [nucleotide strand] 14 | (if (some #(= nucleotide %) valid-nucleotides) 15 | (count 16 | (filter #(= nucleotide %) 17 | strand)) 18 | (throw (Throwable.)))) 19 | 20 | (defn nucleotide-counts 21 | "Count all nucleotide in a strand" 22 | [strand] 23 | (merge {\A 0 \C 0 \G 0 \T 0} 24 | (frequencies "GGGGGAACCCGG"))) 25 | 26 | #_{:clj-kondo/ignore [:redefined-var :clojure-lsp/unused-public-var]} 27 | (comment 28 | 29 | ;; Count the occurances 30 | 31 | "GGGGGTAACCCGG" 32 | 33 | (count 34 | (filter (fn [nucleotide] (= nucleotide \A)) 35 | "GGGGGTAACCCGG")) 36 | 37 | ;; Define the data 38 | 39 | (def valid-nucleotides 40 | "Characters representing valid nucleotides" 41 | [\A \C \G \T]) 42 | 43 | ;; Exception handling required 44 | ;; (throw (Throwable.)) if nucleotide is \X 45 | 46 | ;; Or use a predicate with some (some element? in the sequence) 47 | (some #(= \G %) valid-nucleotides) 48 | 49 | (some #{\G} valid-nucleotides) 50 | 51 | (defn count-of-nucleotide-in-strand 52 | [nucleotide strand] 53 | (if (some #(= nucleotide %) valid-nucleotides) 54 | (count 55 | (filter #(= nucleotide % 56 | strand 57 | (throw (Throwable.))))))) 58 | 59 | (count-of-nucleotide-in-strand \T "GGGGGTAACCCGG") 60 | 61 | ;; Design the second function 62 | 63 | ;; How often does a nucleotide appear 64 | 65 | (map 66 | #(if (= % \A) 1 0) 67 | valid-nucleotides) 68 | 69 | ;; Add the result to get the total count 70 | 71 | ;; Is there a more elegant way? 72 | 73 | (filter #(= % \A) valid-nucleotides) 74 | 75 | ;; Count the elements in the returned sequence for the total 76 | 77 | ;; Design the second function 78 | 79 | ;; How often does a nucleotide appear 80 | ;; NOTE: zero must be returned when there are no appearences 81 | 82 | ;; Return value always in the form 83 | {\A 20, \T 21, \G 17, \C 12} 84 | 85 | ;; Hammock time... 86 | 87 | ;; How often does something appear, 88 | ;; how frequenct is it? 89 | ;; Is there a clojure standard library for that (approx 700 functions) 90 | ;; https://clojure-docs.org/ 91 | 92 | (frequencies "GGGGGAACCCGG" 93 | 94 | ;; If there are missing nucleotides then there is no answer 95 | 96 | ;; What if there is a starting point 97 | 98 | {\A 0 \C 0 \G 0 \T 0}) 99 | 100 | ;; Then merge the result of frequencies 101 | 102 | (merge {\A 0 \C 0 \G 0 \T 0} 103 | (frequencies "GGGGGAACCCGG") 104 | 105 | ;; Update the function definition and run tests 106 | 107 | #_() ; End of rich comment 108 | 109 | ;; --------------------------------------------------------- 110 | ;; Solution 111 | 112 | #_(defn count-of-nucleotide-in-strand [nucleotide strand] 113 | (if (= \X nucleotide) 114 | (throw (Throwable.)) 115 | (count (filter #(= nucleotide %) strand)))) 116 | 117 | #_(defn nucleotide-counts [strand] 118 | (apply merge 119 | (for [nucleotide strand] 120 | {nucleotide 121 | (count-of-nucleotide-in-strand nucleotide strand)}))) 122 | 123 | #_(defn count-of-nucleotide-in-strand [nucleotide strand] 124 | (if (some (set nucleotide) valid-nucleotides) 125 | (count (filter #(= nucleotide %) strand)) 126 | (throw (Throwable.)))))) 127 | 128 | ;; End of Solution 129 | ;; --------------------------------------------------------- 130 | 131 | (comment) 132 | 133 | #_{:clj-kondo/ignore [:redefined-var]} 134 | 135 | #_() ; End of rich comment 136 | 137 | ;; (defn count-of-nucleotide-in-strand [nucleotide strand] 138 | ;; (get (frequencies strand) nucleotide (throw (Throwable.)))) 139 | 140 | ;; add a guard condition if passed an empty strand 141 | ;; returning zero 142 | 143 | ;; (defn count-of-nucleotide-in-strand [nucleotide strand] 144 | ;; (if (empty? strand) 145 | ;; 0 146 | ;; (get (frequencies strand) nucleotide (throw (Throwable.))))) 147 | 148 | ;; (count-of-nucleotide-in-strand \T "GGGGGTAACCCGG") 149 | 150 | ;; using a function as the not-found 151 | ;; (get {:a 1 :b 2} :b (throw (Throwable.))) 152 | 153 | ;; unfortunately the throw expression is evaluated before the get 154 | ;; so we always get a Throwable exception even if the key is in the hash-map. 155 | ;; We can fix that with an or 156 | 157 | ;; (defn count-of-nucleotide-in-strand [nucleotide strand] 158 | ;; (if (empty? strand) 159 | ;; 0 160 | ;; (or (get (frequencies strand) nucleotide) 161 | ;; (throw (Throwable.))))) 162 | ;; 163 | ;; (defn nucleotide-counts [strand] 164 | ;; (frequencies strand)) 165 | ;; 166 | ;; fails the empty case, 167 | ;; but we can add an empty result as the default value 168 | ;; and merge it with the resulting frequencies 169 | 170 | ;; (defn nucleotide-counts [strand] 171 | ;; (merge {\A 0 \C 0 \G 0 \T 0} (frequencies strand))) 172 | ;; 173 | -------------------------------------------------------------------------------- /nucleotide-count/src/nucleotide_count.clj: -------------------------------------------------------------------------------- 1 | (ns nucleotide-count) 2 | 3 | #_(defn count-of-nucleotide-in-strand [nucleotide strand] 4 | (if (= \X nucleotide) 5 | (throw (Throwable.)) 6 | (count (filter #(= nucleotide %) strand)))) 7 | 8 | (count-of-nucleotide-in-strand \A "ACGTA") 9 | 10 | 11 | #_(defn nucleotide-counts [strand] 12 | (apply merge 13 | (for [nucleotide strand] 14 | {nucleotide 15 | (count-of-nucleotide-in-strand nucleotide strand)}))) 16 | 17 | 18 | ;; for produces repeated results 19 | ;; this seems a bit brute force 20 | ;; but we get the right result by merging 21 | 22 | #_(merge {\A 20} 23 | {\G 17} 24 | {\C 12} 25 | {\T 21} 26 | {\T 21} 27 | {\T 21} 28 | {\T 21} 29 | {\C 12} 30 | {\A 20} 31 | {\T 21} 32 | {\T 21} 33 | {\C 12} 34 | {\T 21} 35 | {\G 17} 36 | {\A 20} 37 | {\C 12} 38 | {\T 21} 39 | {\G 17} 40 | {\C 12} 41 | {\A 20} 42 | {\A 20} 43 | {\C 12} 44 | {\G 17} 45 | {\G 17} 46 | {\G 17} 47 | {\C 12} 48 | {\A 20} 49 | {\A 20} 50 | {\T 21} 51 | {\A 20} 52 | {\T 21} 53 | {\G 17} 54 | {\T 21} 55 | {\C 12} 56 | {\T 21} 57 | {\C 12} 58 | {\T 21} 59 | {\G 17} 60 | {\T 21} 61 | {\G 17} 62 | {\T 21} 63 | {\G 17} 64 | {\G 17} 65 | {\A 20} 66 | {\T 21} 67 | {\T 21} 68 | {\A 20} 69 | {\A 20} 70 | {\A 20} 71 | {\A 20} 72 | {\A 20} 73 | {\A 20} 74 | {\A 20} 75 | {\G 17} 76 | {\A 20} 77 | {\G 17} 78 | {\T 21} 79 | {\G 17} 80 | {\T 21} 81 | {\C 12} 82 | {\T 21} 83 | {\G 17} 84 | {\A 20} 85 | {\T 21} 86 | {\A 20} 87 | {\G 17} 88 | {\C 12} 89 | {\A 20} 90 | {\G 17} 91 | {\C 12}) 92 | 93 | 94 | ;; specifying the specific valid nucleotides 95 | 96 | 97 | (def valid-nucleotides 98 | [\A \C \G \T]) 99 | 100 | 101 | ;; we can filter the strand for the specific values 102 | ;; this avoids repeats, 103 | ;; also returns zero counts when nucleotide is not in strand 104 | 105 | 106 | #_(defn nucleotide-counts [strand] 107 | (apply merge 108 | (for [nucleotide valid-nucleotides] 109 | {nucleotide 110 | (count-of-nucleotide-in-strand nucleotide strand)}))) 111 | 112 | 113 | ;; without the for macro 114 | 115 | #_(map 116 | (fn [nucleotide] 117 | {nucleotide 118 | (count-of-nucleotide-in-strand nucleotide "GGGGGTAACCCGG")}) 119 | valid-nucleotides) 120 | 121 | #_(defn nucleotide-counts [strand] 122 | (apply merge 123 | (map 124 | (fn [nucleotide] 125 | {nucleotide 126 | (count-of-nucleotide-in-strand nucleotide strand)}) 127 | valid-nucleotides))) 128 | 129 | 130 | ;;using some nice abstractions 131 | 132 | (frequencies "AGCTTTTCATTCTGACTGCAACGGGCAATATGTCTCTGTGTGGATTAAAAAAAGAGTGTCTGATAGCAGC") 133 | ;; => {\A 20, \G 17, \C 12, \T 21} 134 | 135 | 136 | 137 | (defn count-of-nucleotide-in-strand [nucleotide strand] 138 | (get (frequencies strand) nucleotide)) 139 | 140 | ;; need to add exception handling 141 | 142 | (defn count-of-nucleotide-in-strand [nucleotide strand] 143 | (get (frequencies strand) nucleotide (throw (Throwable.)))) 144 | 145 | (get {} :a "doh!") 146 | 147 | ;; add a guard condition if passed an empty strand 148 | ;; returning zero 149 | 150 | (defn count-of-nucleotide-in-strand [nucleotide strand] 151 | (if (empty? strand) 152 | 0 153 | (get (frequencies strand) nucleotide (throw (Throwable.))))) 154 | 155 | #_(count-of-nucleotide-in-strand \T "GGGGGTAACCCGG") 156 | 157 | ;; using a function as the not-found 158 | (get {:a 1 :b 2} :c '(throw (Throwable.))) 159 | 160 | ;; unfortunately the throw expression is evaluated before the get 161 | ;; so we always get a Throwable exception even if the key is in the hash-map. 162 | ;; We can fix that with an or 163 | 164 | (defn count-of-nucleotide-in-strand [nucleotide strand] 165 | (if (empty? strand) 166 | 0 167 | (or (get (frequencies strand) nucleotide) 168 | (throw (Throwable.))))) 169 | 170 | (defn nucleotide-counts [strand] 171 | (frequencies strand)) 172 | 173 | (nucleotide-counts "GGG") 174 | 175 | ;; fails the empty case, 176 | ;; but we can add an empty result as the default value 177 | ;; and merge it with the resulting frequencies 178 | 179 | 180 | (defn nucleotide-counts [strand] 181 | (merge {\A 0 \C 0 \G 0 \T 0} (frequencies strand))) 182 | 183 | 184 | 185 | ;; Rich comment block with redefined vars ignored 186 | #_{:clj-kondo/ignore [:redefined-var]} 187 | (comment 188 | 189 | (def valid-bases {\A 0, \C 0, \G 0, \T 0}) 190 | 191 | (defn nucleotide-counts [strand] 192 | {:post [(= #{\A \T \C \G} (set (keys %)))]} 193 | (merge valid-bases (frequencies strand))) 194 | 195 | (defn count-of-nucleotide-in-strand [nucleotide strand] 196 | {:pre [(contains? valid-bases nucleotide)]} 197 | ((nucleotide-counts strand) nucleotide)) 198 | 199 | ) ;; End of rich comment block 200 | 201 | 202 | (def invalid-number "0000000000") 203 | 204 | 205 | 206 | 207 | ;; Rich comment block with redefined vars ignored 208 | #_{:clj-kondo/ignore [:redefined-var]} 209 | (comment 210 | 211 | ;; phone numbers code 212 | (defn number [num] 213 | (let [valid-number (apply str (re-seq #"\d" num))] 214 | (case (count valid-number) 215 | 10 valid-number 216 | 11 (if (clojure.string/starts-with? valid-number "1") (subs valid-number 1) invalid-number) 217 | invalid-number))) 218 | 219 | (number "1234567890") 220 | 221 | 222 | ) ;; End of rich comment block 223 | -------------------------------------------------------------------------------- /series/src/design_journal.clj: -------------------------------------------------------------------------------- 1 | (ns design-journal) 2 | 3 | ;; Given a string of digits, output all the contiguous substrings of length n in that string in the order that they appear. 4 | ;; For example, the string "49142" has the following 3-digit series: 5 | ;; "491" 6 | ;; "914" 7 | ;; "142" 8 | 9 | ;; And the following 4-digit series: 10 | ;; "4914" 11 | ;; "9142" 12 | 13 | ;; And if you ask for a 6-digit series from a 5-digit string, you deserve whatever you get. 14 | ;; Note that these series are only required to occupy adjacent positions in the input; the digits need not be numerically consecutive. 15 | 16 | 17 | ;; Deconstruct the problem 18 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 19 | 20 | ;; Lets look at the project Euler challenge 21 | ;; https://projecteuler.net/problem=8 22 | 23 | ;; So we want to find adjacent number sequence 24 | ;; from a potentially large sequence 25 | 26 | ;; In the exercisim challenge our numbers are strings 27 | ;; so that is a little bit easier to divide up. 28 | 29 | 30 | ;; Test data 31 | ;; (slices "" 1) 32 | ;; (slices "123" 0) 33 | ;; (slices "123" 1000) 34 | ;; (slices "123" 3) 35 | ;; (slices "12345" 3) 36 | 37 | 38 | 39 | ;; REPL experiments 40 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 41 | 42 | ;; To divide up a sequence we can use partition 43 | ;; setting how many partitions of the data we want to make 44 | 45 | (partition 5 (range 20)) 46 | ;; => ((0 1 2 3 4) (5 6 7 8 9) (10 11 12 13 14) (15 16 17 18 19)) 47 | 48 | ;; partition can loose data 49 | 50 | (partition 6 (range 20)) 51 | ;; => ((0 1 2 3 4 5) (6 7 8 9 10 11) (12 13 14 15 16 17)) 52 | 53 | ;; so there is partition all 54 | (partition-all 6 (range 20)) 55 | ;; => ((0 1 2 3 4 5) (6 7 8 9 10 11) (12 13 14 15 16 17) (18 19)) 56 | 57 | 58 | ;; We want to create a range of sequences though... 59 | ;; and we can still use partition 60 | ;; with a step 61 | ;; So far there is an implied step of the partition size 62 | 63 | 64 | (partition 4 2 (range 20)) 65 | ;; => ((0 1 2 3) (2 3 4 5) (4 5 6 7) (6 7 8 9) (8 9 10 11) (10 11 12 13) (12 13 14 15) (14 15 16 17) (16 17 18 19)) 66 | 67 | ;; For our challenge we want all the sequences 68 | (partition 1 1 (range 20)) 69 | 70 | 71 | (partition 3 1 (range 20)) 72 | ;; => ((0 1 2) (1 2 3) (2 3 4) (3 4 5) (4 5 6) (5 6 7) (6 7 8) (7 8 9) (8 9 10) (9 10 11) (10 11 12) (11 12 13) (12 13 14) (13 14 15) (14 15 16) (15 16 17) (16 17 18) (17 18 19)) 73 | 74 | (partition 100 1 (range 20)) 75 | ;; => () 76 | 77 | (partition-all 100 1 (range 20)) 78 | ;; => ((0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19) (1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19) (2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19) (3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19) (4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19) (5 6 7 8 9 10 11 12 13 14 15 16 17 18 19) (6 7 8 9 10 11 12 13 14 15 16 17 18 19) (7 8 9 10 11 12 13 14 15 16 17 18 19) (8 9 10 11 12 13 14 15 16 17 18 19) (9 10 11 12 13 14 15 16 17 18 19) (10 11 12 13 14 15 16 17 18 19) (11 12 13 14 15 16 17 18 19) (12 13 14 15 16 17 18 19) (13 14 15 16 17 18 19) (14 15 16 17 18 19) (15 16 17 18 19) (16 17 18 19) (17 18 19) (18 19) (19)) 79 | 80 | 81 | 82 | 83 | ;; Test data 84 | ;; (slices "123" 0) 85 | 86 | (partition 0 1 "123") 87 | ;; => (() () ()) 88 | 89 | (= [] '()) 90 | 91 | (= [] (partition 0 1 "123")) 92 | ;; => false 93 | 94 | (= [] (flatten (partition 0 1 "123"))) 95 | 96 | ;; flatten usually feels a bit naughty 97 | ;; and its a bit of a sledgehammer 98 | 99 | ;; Tests 1 and 3 pass though 100 | ;; Test 3 is: 101 | ;; (slices "123" 1000) 102 | 103 | 104 | ;; Test 2 105 | ;; (slices "123" 3) 106 | 107 | (partition 3 1 "123") 108 | ;; => ((\1 \2 \3)) 109 | 110 | (partition 3 3 "123") 111 | ;; => ((\1 \2 \3)) 112 | 113 | 114 | (flatten (partition 3 1 "123")) 115 | 116 | (into [] 117 | (partition 3 1 "123")) 118 | 119 | (map str 120 | (partition 3 1 "123")) 121 | 122 | 123 | (map #(apply str %) 124 | (partition 3 1 "123")) 125 | 126 | 127 | 128 | 129 | ;; Try this with the tests 130 | (defn slices 131 | [string length] 132 | (map #(apply str %) 133 | (partition length 1 string))) 134 | 135 | 136 | 137 | 138 | ;; Passes all tests except the zero length 139 | ;; actually returning ("" "" "") 140 | 141 | 142 | ;; We could put an if expression 143 | ;; to check for a zero length 144 | 145 | (defn slices 146 | [string length] 147 | (if (= 0 length) 148 | [""] 149 | (map #(apply str %1) 150 | (partition length 1 string)))) 151 | 152 | 153 | (defn slices 154 | [string length] 155 | (if (zero? length) 156 | [""] 157 | (map #(apply str %1) 158 | (partition length 1 string)))) 159 | 160 | 161 | 162 | 163 | ;; These values are all the same 164 | ;; so if we can rule out duplicates 165 | ;; then maybe we have a winner 166 | 167 | (map identity [:one :two :one]) 168 | 169 | (apply map identity '("" "" "")) 170 | 171 | 172 | ;; One way to find Clojure functions is to extend your vocabulary 173 | ;; Search for duplicate in the browser 174 | ;; check its opposites 175 | ;; different 176 | ;; distinct 177 | 178 | (distinct) 179 | 180 | 181 | (distinct 182 | (map #(apply str %) 183 | (partition 0 1 "123"))) 184 | 185 | 186 | ;; failing different tests 187 | 188 | 189 | (reduce conj '("")) 190 | 191 | (reduce conj 192 | (distinct 193 | (map #(apply str %) 194 | (partition 0 1 "123")))) 195 | 196 | 197 | (into [] 198 | (distinct 199 | (map #(apply str %) 200 | (partition 0 1 "123")))) 201 | 202 | 203 | 204 | (defn slices 205 | [string length] 206 | (into [] 207 | (distinct 208 | (map #(apply str %) 209 | (partition length 1 string))) )) 210 | 211 | 212 | ;; Answers summary 213 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 214 | 215 | #_{:clj-kondo/ignore [:redefined-var]} 216 | (comment 217 | 218 | (defn slices 219 | [string length] 220 | (into [] 221 | (distinct 222 | (map #(apply str %) 223 | (partition length 1 string))) )) 224 | 225 | 226 | 227 | (defn slices [string length] 228 | (->> string 229 | (partition length 1) 230 | (map #(apply str %)) 231 | (distinct) 232 | (into []))) 233 | 234 | ) ;; End of rich comment block 235 | 236 | 237 | 238 | ;; Rich comment block with redefined vars ignored 239 | #_{:clj-kondo/ignore [:redefined-var]} 240 | (comment 241 | 242 | 243 | 244 | (defn slices 245 | [string length] 246 | (into [] 247 | (distinct 248 | (map #(apply str %) 249 | (partition length 1 string))))) 250 | 251 | ) ;; End of rich comment block 252 | 253 | 254 | ;; Rich comment block with redefined vars ignored 255 | #_{:clj-kondo/ignore [:redefined-var]} 256 | (comment 257 | 258 | 259 | (defn slices [string length] 260 | (->> 261 | (partition length 1 string) 262 | (map (partial apply str)) distinct)) 263 | 264 | ) ;; End of rich comment block 265 | 266 | 267 | 268 | 269 | #_(defn slices [string length] 270 | (if (zero? length) 271 | [""] 272 | (map string/join (partition length 1 string)))) 273 | 274 | (defn slices [string length] 275 | (distinct 276 | (map (partial apply str) 277 | (partition length 1 string)))) 278 | 279 | 280 | #_(defn slices [string length] 281 | (->> 282 | (partition length 1 string) 283 | (map (partial apply str)) 284 | distinct)) 285 | 286 | (slices "123" 0) 287 | 288 | 289 | (defn slices [string length] 290 | (distinct 291 | (map (partial apply str) 292 | (partition length 1 string)))) 293 | 294 | (defn slices [string length] 295 | (distinct 296 | (map #(apply str %) 297 | (partition length 1 string)))) 298 | -------------------------------------------------------------------------------- /clock/test/clock_test.clj: -------------------------------------------------------------------------------- 1 | (ns clock-test 2 | (:require [clock :refer :all] 3 | [clojure.test :refer [deftest testing is]])) 4 | 5 | (deftest create-clock-test 6 | 7 | (testing "on the hour" 8 | (let [test-clock (clock->string (clock 8 0))] 9 | (is (= "08:00" test-clock)))) 10 | (testing "past the hour" 11 | (let [test-clock (clock->string (clock 11 9))] 12 | (is (= "11:09" test-clock)))) 13 | (testing "midnight is zero hours" 14 | (let [test-clock (clock->string (clock 24 0))] 15 | (is (= "00:00" test-clock)))) 16 | (testing "hour rolls over" 17 | (let [test-clock (clock->string (clock 25 0))] 18 | (is (= "01:00" test-clock)))) 19 | (testing "hour rolls over continuously" 20 | (let [test-clock (clock->string (clock 100 0))] 21 | (is (= "04:00" test-clock)))) 22 | (testing "sixty minutes is next hour" 23 | (let [test-clock (clock->string (clock 1 60))] 24 | (is (= "02:00" test-clock)))) 25 | (testing "minutes roll over" 26 | (let [test-clock (clock->string (clock 0 160))] 27 | (is (= "02:40" test-clock)))) 28 | (testing "minutes roll over continuously" 29 | (let [test-clock (clock->string (clock 0 1723))] 30 | (is (= "04:43" test-clock)))) 31 | (testing "hour and minutes roll over" 32 | (let [test-clock (clock->string (clock 25 160))] 33 | (is (= "03:40" test-clock)))) 34 | (testing "hour and minutes roll over continuously" 35 | (let [test-clock (clock->string (clock 201 3001))] 36 | (is (= "11:01" test-clock)))) 37 | (testing "hour and minutes roll over to exactly midnight" 38 | (let [test-clock (clock->string (clock 72 8640))] 39 | (is (= "00:00" test-clock)))) 40 | (testing "negative hour" 41 | (let [test-clock (clock->string (clock -1 15))] 42 | (is (= "23:15" test-clock)))) 43 | (testing "negative hour rolls over" 44 | (let [test-clock (clock->string (clock -25 0))] 45 | (is (= "23:00" test-clock)))) 46 | (testing "negative hour rolls over continuously" 47 | (let [test-clock (clock->string (clock -91 0))] 48 | (is (= "05:00" test-clock)))) 49 | (testing "negative minutes" 50 | (let [test-clock (clock->string (clock 1 -40))] 51 | (is (= "00:20" test-clock)))) 52 | (testing "negative minutes roll over" 53 | (let [test-clock (clock->string (clock 1 -160))] 54 | (is (= "22:20" test-clock)))) 55 | (testing "negative minutes roll over continuously" 56 | (let [test-clock (clock->string (clock 1 -4820))] 57 | (is (= "16:40" test-clock)))) 58 | (testing "negative hour and minutes both roll over" 59 | (let [test-clock (clock->string (clock -25 -160))] 60 | (is (= "20:20" test-clock)))) 61 | (testing "negative hour and minutes both roll over continuously" 62 | (let [test-clock (clock->string (clock -121 -5810))] 63 | (is (= "22:10" test-clock))))) 64 | 65 | (deftest add-time-test 66 | 67 | (testing "add minutes" 68 | (let [test-clock (clock->string (add-time (clock 10 0) 3))] 69 | (is (= "10:03" test-clock)))) 70 | (testing "add no minutes" 71 | (let [test-clock (clock->string (add-time (clock 6 41) 0))] 72 | (is (= "06:41" test-clock)))) 73 | (testing "add to next hour" 74 | (let [test-clock (clock->string (add-time (clock 0 45) 40))] 75 | (is (= "01:25" test-clock)))) 76 | (testing "add more than one hour" 77 | (let [test-clock (clock->string (add-time (clock 10 0) 61))] 78 | (is (= "11:01" test-clock)))) 79 | (testing "add more than two hours with carry" 80 | (let [test-clock (clock->string (add-time (clock 0 45) 160))] 81 | (is (= "03:25" test-clock)))) 82 | (testing "add across midnight" 83 | (let [test-clock (clock->string (add-time (clock 23 59) 2))] 84 | (is (= "00:01" test-clock)))) 85 | (testing "add more than one day (1500 min = 25 hrs)" 86 | (let [test-clock (clock->string (add-time (clock 5 32) 1500))] 87 | (is (= "06:32" test-clock)))) 88 | (testing "add more than two days" 89 | (let [test-clock (clock->string (add-time (clock 1 1) 3500))] 90 | (is (= "11:21" test-clock)))) 91 | (testing "subtract minutes" 92 | (let [test-clock (clock->string (add-time (clock 10 3) -3))] 93 | (is (= "10:00" test-clock)))) 94 | (testing "subtract to previous hour" 95 | (let [test-clock (clock->string (add-time (clock 10 3) -30))] 96 | (is (= "09:33" test-clock)))) 97 | (testing "subtract more than an hour" 98 | (let [test-clock (clock->string (add-time (clock 10 3) -70))] 99 | (is (= "08:53" test-clock)))) 100 | (testing "subtract across midnight" 101 | (let [test-clock (clock->string (add-time (clock 0 3) -4))] 102 | (is (= "23:59" test-clock)))) 103 | (testing "subtract more than two hours" 104 | (let [test-clock (clock->string (add-time (clock 0 0) -160))] 105 | (is (= "21:20" test-clock)))) 106 | (testing "subtract more than two hours with borrow" 107 | (let [test-clock (clock->string (add-time (clock 6 15) -160))] 108 | (is (= "03:35" test-clock)))) 109 | (testing "subtract more than one day (1500 min = 25 hrs)" 110 | (let [test-clock (clock->string (add-time (clock 5 32) -1500))] 111 | (is (= "04:32" test-clock)))) 112 | (testing "subtract more than two days" 113 | (let [test-clock (clock->string (add-time (clock 2 20) -3000))] 114 | (is (= "00:20" test-clock))))) 115 | 116 | (deftest equal-clock-test 117 | (testing "clocks with same time" 118 | (let [clock1 (clock 15 37) 119 | clock2 (clock 15 37)] 120 | (is (= clock1 clock2)))) 121 | (testing "clocks a minute apart" 122 | (let [clock1 (clock 15 36) 123 | clock2 (clock 15 37)] 124 | (is (not= clock1 clock2)))) 125 | (testing "clocks an hour apart" 126 | (let [clock1 (clock 14 37) 127 | clock2 (clock 15 37)] 128 | (is (not= clock1 clock2)))) 129 | (testing "clocks with hour overflow" 130 | (let [clock1 (clock 10 37) 131 | clock2 (clock 34 37)] 132 | (is (= clock1 clock2)))) 133 | (testing "clocks with hour overflow by several days" 134 | (let [clock1 (clock 3 11) 135 | clock2 (clock 99 11)] 136 | (is (= clock1 clock2)))) 137 | (testing "clocks with negative hour" 138 | (let [clock1 (clock 22 40) 139 | clock2 (clock -2 40)] 140 | (is (= clock1 clock2)))) 141 | (testing "clocks with negative hour that wraps" 142 | (let [clock1 (clock 17 3) 143 | clock2 (clock -31 3)] 144 | (is (= clock1 clock2)))) 145 | (testing "clocks with negative hour that wraps multiple times" 146 | (let [clock1 (clock 13 49) 147 | clock2 (clock -83 49)] 148 | (is (= clock1 clock2)))) 149 | (testing "clocks with minute overflow" 150 | (let [clock1 (clock 0 1) 151 | clock2 (clock 0 1441)] 152 | (is (= clock1 clock2)))) 153 | (testing "clocks with minute overflow by several days" 154 | (let [clock1 (clock 2 2) 155 | clock2 (clock 2 4322)] 156 | (is (= clock1 clock2)))) 157 | (testing "clocks with negative minute" 158 | (let [clock1 (clock 2 40) 159 | clock2 (clock 3 -20)] 160 | (is (= clock1 clock2)))) 161 | (testing "clocks with negative minute that wraps" 162 | (let [clock1 (clock 4 10) 163 | clock2 (clock 5 -1490)] 164 | (is (= clock1 clock2)))) 165 | (testing "clocks with negative minute that wraps multiple times" 166 | (let [clock1 (clock 6 15) 167 | clock2 (clock 6 -4305)] 168 | (is (= clock1 clock2)))) 169 | (testing "clocks with negative hours and minutes" 170 | (let [clock1 (clock 7 32) 171 | clock2 (clock -12 -268)] 172 | (is (= clock1 clock2)))) 173 | (testing "clocks with negative hours and minutes that wrap" 174 | (let [clock1 (clock 18 7) 175 | clock2 (clock -54 -11513)] 176 | (is (= clock1 clock2))))) 177 | -------------------------------------------------------------------------------- /space-age/src/design_journal_redux.clj: -------------------------------------------------------------------------------- 1 | (ns design-journal-redux) 2 | 3 | ;; Namespace api from the tests 4 | 5 | (def on-mercury nil) 6 | (def on-venus nil) 7 | (def on-earth nil) 8 | (def on-mars nil) 9 | (def on-jupiter nil) 10 | (def on-saturn nil) 11 | (def on-neptune nil) 12 | (def on-uranus nil) 13 | 14 | ;; --------------------------------------------------------- 15 | ;; REPL experiments 16 | 17 | (comment 18 | ;; Start with just Earth 19 | 20 | ;; Calculate earths orbital period in seconds 21 | ;; Calculate the hours, minutes and seconds 22 | 23 | (def orbital-period 24 | {:earth 365.25}) 25 | 26 | ;; hours-in-a-year 27 | (* (:earth orbital-period) 24) 28 | ;; => 8766.0 29 | 30 | ;; minutes in a year 31 | (* (:earth orbital-period) 24 60) 32 | ;; => 525960.0 33 | 34 | ;; seconds in a year 35 | (* (:earth orbital-period) 24 60 60) 36 | ;; => 3.15576E7 37 | 38 | (defn seconds->years 39 | [seconds] 40 | (/ seconds (:earth orbital-period) 24 60 60)) 41 | 42 | ;; The other planets 43 | 44 | ;; All other planet orbits are relative to that of earth, 45 | ;; so multiplying the Earth orbit by another planets orbit period 46 | ;; should give the correct answer 47 | 48 | ;; Create a data structure to hold the relative planet orbits 49 | 50 | (def orbital-period 51 | "Orbital periods for planets in the sol system, 52 | relative to the orbit of Earth" 53 | {:earth 365.25 54 | :jupiter 11.862615 55 | :mars 1.8808158 56 | :mercury 0.2408467 57 | :neptune 164.79132 58 | :saturn 29.447498 59 | :uranus 84.016846 60 | :venus 0.61519726}) 61 | 62 | ;; Then use the seconds->years function definition, 63 | ;; but multiply the relative orbit with the Earth orbit 64 | ;; So the on-mercury function definition would be: 65 | 66 | (defn on-mercury [seconds] 67 | (/ seconds (* (:earth orbital-period) (:mercury orbital-period)) 24 60 60)) 68 | 69 | (on-mercury 2134835688) 70 | ;; => 280.87933423985845 71 | 72 | ;; Define a relative age function 73 | 74 | (defn relative-age 75 | [realtve-orbit] 76 | (fn [seconds] 77 | (let [earth-years (seconds-to-earth-years seconds)] 78 | (/ earth-years realtve-orbit)))) 79 | 80 | (defn seconds-to-earth-years 81 | "Convert sections to earth years" 82 | [seconds] 83 | (/ seconds 31557600.0))) 84 | 85 | ;; --------------------------------------------------------- 86 | ;; Solutions 87 | 88 | ;; (defn seconds-to-earth-years 89 | ;; "Convert sections to earth years" 90 | ;; [seconds] 91 | ;; (/ seconds 31557600.0)) 92 | ;; 93 | ;; (defn relative-age 94 | ;; "Return function to calculate age 95 | ;; based on each planets relative orbit" 96 | ;; [realtve-orbit] 97 | ;; (fn [seconds] 98 | ;; (let [earth-years (seconds-to-earth-years seconds)] 99 | ;; (/ earth-years realtve-orbit)))) 100 | ;; 101 | ;; 102 | ;; (def on-mercury (relative-age 0.240846)) 103 | ;; (def on-venus (relative-age 0.6151972)) 104 | ;; (def on-earth (relative-age 1)) 105 | ;; (def on-mars (relative-age 1.8808158)) 106 | ;; (def on-jupiter (relative-age 11.862615)) 107 | ;; (def on-saturn (relative-age 29.447498)) 108 | ;; (def on-neptune (relative-age 164.79132)) 109 | ;; (def on-uranus (relative-age 84.016846)) 110 | 111 | ;; --------------------------------------------------------- 112 | ;; Alternative REPL experiments 113 | 114 | #_{:clj-kondo/ignore [:redefined-var]} 115 | (comment 116 | 117 | ;; How to represent the data 118 | 119 | ;; Eath orbit is the number of days 120 | (def earth-orbit 365.25) 121 | 122 | ;; Planet orbits are relative to the earth 123 | 124 | (def relative-orbital-period 125 | "Orbital periods for planets in the sol system, 126 | relative to the orbit of Earth" 127 | {:jupiter 11.862615 128 | :mars 1.8808158 129 | :mercury 0.2408467 130 | :neptune 164.79132 131 | :saturn 29.447498 132 | :uranus 84.016846 133 | :venus 0.61519726}) 134 | 135 | ;; Then use the seconds->years function definition, 136 | ;; but multiply the relative orbit with the Earth orbit 137 | ;; So the on-mercury function definition would be: 138 | 139 | (defn on-mercury [seconds] 140 | (/ seconds 141 | (* earth-orbit 142 | (:mercury relative-orbital-period)) 143 | 24 60 60)) 144 | 145 | (on-mercury 2134835688) 146 | ;; => 280.87933423985845 147 | 148 | ;; Refactor 149 | ;; Reduce repeated calculations code 150 | 151 | ;; Transform the data 152 | 153 | ;; * Earth-orbital-period 154 | ;; relative-planet-orbital-period 155 | ;; hours minutes seconds 156 | 157 | (defn relative-orbit->orbit-duration 158 | "Calculate a planets orbital duration, 159 | given its relative orbit to the earth" 160 | [relative-orbit] 161 | (let [earth-orbital-period 365.25] 162 | (* earth-orbital-period 163 | relative-orbit 164 | 24 60 60))) 165 | 166 | (relative-orbit->orbit-duration 0.2408467) 167 | ;; 7600543.81992 168 | 169 | ;; On-mercury test seconds: 2134835688 170 | (/ 2134835688 7600543.81992) 171 | 172 | ;; Define a function to iterate over all of the relative orbits 173 | 174 | ;; Map: relative-orbital-period 175 | ;; Function: relative-orbit->orbit-duration 176 | 177 | (defn update-orbital-period 178 | [m f] 179 | (reduce-kv 180 | (fn [m k v] (assoc m k (f v))) 181 | {} 182 | m)) 183 | 184 | (update-orbital-period 185 | relative-orbital-period 186 | relative-orbit->orbit-duration) 187 | 188 | ;; Define a transformed map 189 | 190 | (def planet-orbit-duration 191 | (update-orbital-period 192 | relative-orbital-period 193 | relative-orbit->orbit-duration)) 194 | 195 | planet-orbit-duration 196 | ;;{:jupiter 3.7435565912399995E8, 197 | ;; :mars 5.9354032690079994E7, 198 | ;; :mercury 7600543.81992, 199 | ;; :neptune 5.200418560032E9, 200 | ;; :saturn 9.292923628848001E8, 201 | ;; :uranus 2.6513700193296E9, 202 | ;; :venus 1.9414149052176E7} 203 | 204 | ;; Add Earth duration 205 | (def planet-orbit-duration 206 | (merge 207 | (update-orbital-period 208 | relative-orbital-period 209 | relative-orbit->orbit-duration) 210 | {:earth (* earth-orbit 24 60 60)})) 211 | 212 | planet-orbit-duration 213 | ;;{:jupiter 3.7435565912399995E8, 214 | ;; :mars 5.9354032690079994E7, 215 | ;; :mercury 7600543.81992, 216 | ;; :neptune 5.200418560032E9, 217 | ;; :saturn 9.292923628848001E8, 218 | ;; :uranus 2.6513700193296E9, 219 | ;; :venus 1.9414149052176E7, 220 | ;; :earth 3.15576E7} 221 | 222 | #_()) ; End of rich comment 223 | 224 | ;; End of REPL experiments 225 | ;; --------------------------------------------------------- 226 | 227 | ;; --------------------------------------------------------- 228 | ;; Alternative solutions 229 | 230 | ;; (def earth-orbit 365.25) 231 | ;; 232 | ;; (def relative-orbital-period 233 | ;; "Orbital periods for planets in the sol system, 234 | ;; relative to the orbit of Earth" 235 | ;; {:jupiter 11.862615 236 | ;; :mars 1.8808158 237 | ;; :mercury 0.2408467 238 | ;; :neptune 164.79132 239 | ;; :saturn 29.447498 240 | ;; :uranus 84.016846 241 | ;; :venus 0.61519726}) 242 | ;; 243 | ;; (defn relative-orbit->orbit-duration 244 | ;; "Calculate a planets orbital duration, 245 | ;; given its relative orbit to the earth" 246 | ;; [relative-orbit] 247 | ;; (let [earth-orbital-period 365.25] 248 | ;; (* earth-orbital-period 249 | ;; relative-orbit 250 | ;; 24 60 60))) 251 | ;; 252 | ;; (defn update-orbital-period 253 | ;; [m f] 254 | ;; (reduce-kv 255 | ;; (fn [m k v] (assoc m k (f v))) 256 | ;; {} 257 | ;; m)) 258 | ;; 259 | ;; (def planet-orbit-duration 260 | ;; (merge 261 | ;; (update-orbital-period 262 | ;; relative-orbital-period 263 | ;; relative-orbit->orbit-duration) 264 | ;; {:earth (* earth-orbit 24 60 60)})) 265 | 266 | ;; End of Alternative solutions 267 | ;; --------------------------------------------------------- 268 | -------------------------------------------------------------------------------- /rna-transcription/src/design_journal.clj: -------------------------------------------------------------------------------- 1 | (ns design-journal) 2 | 3 | ;; --------------------------------------------------------- 4 | ;; Initial thought are to create a hash-map as a simple dictionary lookup. 5 | ;; Given a DNA nucleotide, e.g a character from the DNA string, find the key that is the same as the character and return the keys value. 6 | 7 | ;; A state transaction or dictionary 8 | 9 | ;; - `G` -> `C` 10 | ;; - `C` -> `G` 11 | ;; - `T` -> `A` 12 | ;; - `A` -> `U` 13 | 14 | (def dictionary-dna-rna 15 | "Convert DNA to RNA" 16 | {"G" "C" 17 | "C" "G" 18 | "T" "A" 19 | "A" "U"}) 20 | ;; --------------------------------------------------------- 21 | 22 | ;; --------------------------------------------------------- 23 | ;; Write a function to get the value for a given key (dna nucleotide) 24 | 25 | (defn dna-nucleotide 26 | "Convert a specific nucleotide from a DNA strand, 27 | into a nucleotide for an RNA strand" 28 | [dictionary nucleotide] 29 | (get dictionary (str nucleotide))) 30 | 31 | ;; simplify the function by using the hash-map as a function 32 | ;; provide a not-found value if the key is not in the map, so that the resulting RNA code can be checked for bad input data. 33 | 34 | (defn convert-nucleotide 35 | "Convert a specific nucleotide from a DNA strand, 36 | into a nucleotide for an RNA strand" 37 | [dictionary nucleotide] 38 | (dictionary (str nucleotide) "X")) 39 | 40 | ;; To pass the tests the dna string can simply be checkef if it contains X. Alhtough this satisfies the tests, its probably not meeting the full scope of the challenge. 41 | ;; If X is found in the dna string, then return an assertion error, using a string to make the error more informative. 42 | 43 | #_(defn to-rna [dna] 44 | (if (clojure.string/includes? dna "X") 45 | (throw (AssertionError. "Unknown nucleotide")) 46 | (clojure.string/join 47 | (map #(convert-nucleotide dictionary-dna-rna %) dna)))) 48 | ;; --------------------------------------------------------- 49 | 50 | ;; --------------------------------------------------------- 51 | ;; Restart in thinking 52 | 53 | ;; To convert a collection of values, define a hash-map where the keys are the initial value and the values are the transformed value (conversion, encoding, etc). This is often refered to as a dictionary. 54 | 55 | ;; A string can be used as a collection of character values. 56 | 57 | {\G \C \C \G \T \A \A \U} 58 | 59 | ;; Then map this dictionary over the dna sting (collection of characters) to create the RNA transcription. 60 | ;; Use an anonymous function to wrap the dictionary and pass each a character (nucleotide) from the DNA string in turn. 61 | 62 | (defn to-rna 63 | [dna] 64 | (map (fn [nucleotide] (get {\G \C \C \G \T \A \A \U} nucleotide)) 65 | dna)) 66 | 67 | (to-rna "GCTA") 68 | ;; => (\C \G \A \U) 69 | 70 | ;; > #(get {\G \C \C \G \T \A \A \U} %) is the same as using the dictionary hash-map as a function. 71 | 72 | ;; The result is returned as a sequence of characters. 73 | ;; Use `clojure.string/join` to return the RNA value as a string 74 | 75 | (require '[clojure.string]) 76 | 77 | #_{:clj-kondo/ignore [:redefined-var]} 78 | (defn to-rna 79 | [dna] 80 | (clojure.string/join 81 | (map (fn [nucleotide] (get {\G \C \C \G \T \A \A \U} nucleotide)) 82 | dna))) 83 | 84 | (comment 85 | (to-rna "GCTA")) 86 | ;; => "CGAU" 87 | 88 | ;; What about when the nucleotide is invalid? 89 | ;; The hash-map can return a default value, which is nil if not explicitly defined 90 | 91 | ;; How about adding the throw as the not-found value? 92 | 93 | #_{:clj-kondo/ignore [:redefined-var]} 94 | (defn to-rna 95 | [dna] 96 | (clojure.string/join 97 | (map (fn [nucleotide] (get {\G \C \C \G \T \A \A \U} nucleotide 98 | (throw (AssertionError. "Unknown nucleotide")))) 99 | dna))) 100 | 101 | ;; Unfortunately this will evaluate the throw expression regardless of if the nucleotide is found in the hash-map, so always fails. 102 | ;; --------------------------------------------------------- 103 | 104 | ;; --------------------------------------------------------- 105 | ;; `or` function to the rescue 106 | ;; The `or` function will evaluate the first expression and if a true value is returned then any additional expressions are skipped over. 107 | ;; If the first expression returns false or a falsey value, i.e. `nil`, then the next expression is evaluated. 108 | 109 | #_{:clj-kondo/ignore [:redefined-var]} 110 | (defn to-rna 111 | [dna] 112 | (clojure.string/join 113 | (map (fn [nucleotide] (or (get {\G \C \C \G \T \A \A \U} nucleotide) 114 | (throw (AssertionError. "Unknown nucleotide")))) 115 | dna))) 116 | 117 | (comment 118 | (to-rna "GCTA") 119 | ;; => "CGAU" 120 | 121 | (to-rna "GCXA")) 122 | 123 | ;; an AssertionError is thrown as the `X` character does not exist in the dictionary hash-map, so the `get` expression returns `nil`. 124 | ;; --------------------------------------------------------- 125 | 126 | ;; --------------------------------------------------------- 127 | ;; Refactor and streamline 128 | 129 | ;; remove the get 130 | ;; A hash-map can be called as a function and takes a key as an argument. This acts the same as the get function, returning the value associated to a matching key, otherwise returning `nil` or the not-found value if specified. 131 | 132 | #_{:clj-kondo/ignore [:redefined-var]} 133 | (comment 134 | (defn to-rna 135 | [dna] 136 | (clojure.string/join 137 | (map (fn [nucleotide] (or ({\G \C \C \G \T \A \A \U} nucleotide) 138 | (throw (AssertionError. "Unknown nucleotide")))) 139 | dna)))) 140 | 141 | ;; using the anonymous function syntax sugar 142 | ;; `#(* %1 %2) `is the same as `(fn [value1 value2] (+ value1 value2)) `;; the syntax sugar is often use with `map`, `reduce`, `apply` functions as the function definition tends to be compact and of single use. 143 | 144 | ;; If the function definition is more complex or used elsewhere in the namespace, then the `defn` function should be used to define shared behavior. 145 | 146 | (defn to-rna 147 | [dna] 148 | (clojure.string/join 149 | (map #(or ({\G \C \C \G \T \A \A \U} %) 150 | (throw (AssertionError. "Unknown nucleotide"))) 151 | dna))) 152 | 153 | ;; remove the hard-coded hash-map 154 | 155 | (def dictionary-dna-rna {\G \C \C \G \T \A \A \U}) 156 | 157 | #_{:clj-kondo/ignore [:redefined-var]} 158 | (defn to-rna 159 | [dna] 160 | (clojure.string/join 161 | (map #(or (dictionary-dna-rna %) 162 | (throw (AssertionError. "Unknown nucleotide"))) 163 | dna))) 164 | ;; --------------------------------------------------------- 165 | 166 | ;; --------------------------------------------------------- 167 | ;; Pure function approach 168 | 169 | ;; Its beyond the scope of the Exercism challenge, however, its recommended to use pure functions where possible. 170 | ;; A pure function only uses data from its arguments. 171 | ;; Adding a dictionary as an argument to the `to-rna` function would be simple-ident? 172 | ;; requires refactoring the unit tests 173 | ;; Add dna->rna to its own namespace and create parallel tests which use the 2 arity function 174 | 175 | #_{:clj-kondo/ignore [:redefined-var]} 176 | (defn to-rna 177 | [dictionary dna] 178 | (clojure.string/join 179 | (map #(or (dictionary %) 180 | (throw (AssertionError. "Unknown nucleotide"))) 181 | dna))) 182 | 183 | ;; With a dictionary as an argument the function is also more usable, as other dictionaries could be used with the function. 184 | 185 | ;; The function would now be called as follows 186 | 187 | ;; (to-rna dictionary-dna-rna dictionary-dna-rna "GTGAC") 188 | 189 | ;; Other interesting points 190 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 191 | 192 | ;; Convert a string into a sequence 193 | ;; This is a little superfluous as many clojure.core functions will treat a string as a collection of characters by default. 194 | 195 | (into [] "ABCD") 196 | ;; => [\A \B \C \D] 197 | 198 | ;; Checks for nucleotides in the dictionary can be done with the `contains?` function. 199 | ;; This checks to see if the value is a key in the hash-map. 200 | ;; Contains does not check against the values in a map. It also checks different things depending on the type of collection. 201 | 202 | (contains? dictionary-dna-rna "X") 203 | ;; => false 204 | 205 | ;; The conversion could take place on the whole rna sequence and check if a correct sequence has been created 206 | ;; using the map to generate a placeholder value if passed an incorrect nucleotide. 207 | 208 | (convert-nucleotide dictionary-dna-rna "B") 209 | 210 | ;; Using Contains to validate the dna sequence 211 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 212 | ;; Check the dna sequence contains all valid characters 213 | ;; If so, then replace characters with their rna equivalent 214 | 215 | ;; Good approach if replacement of characters use considerable compute resource 216 | ;; compared to validating the dna sequence. 217 | ;; Otherwise the dna sequence is traversed twice, which may not be that efficient. 218 | 219 | ;; Test performance of this approach verses the recommended solution 220 | ;; especially on large dna sequences. https://github.com/hugoduncan/criterium 221 | 222 | (def dna->rna {\G \C 223 | \C \G 224 | \T \A 225 | \A \U}) 226 | 227 | (defn valid-dna? [n] 228 | (contains? dna->rna n)) 229 | 230 | #_{:clj-kondo/ignore [:redefined-var]} 231 | (defn to-rna [dna] 232 | (assert (every? valid-dna? dna)) 233 | (clojure.string/join (replace dna->rna dna))) 234 | 235 | (to-rna "GATTAGA") 236 | -------------------------------------------------------------------------------- /beer-song/src/beer_song.clj: -------------------------------------------------------------------------------- 1 | (ns beer-song 2 | (:require clojure.string)) 3 | 4 | ;; To get the right strings based on the number bottles left, 5 | ;; a cond statement is relatively simple 6 | ;; There are significant context differences preventing a terser solution 7 | 8 | ;; starting point 9 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 10 | 11 | #_(defn verse 12 | "Returns the nth verse of the song." 13 | [num] 14 | ;; your code here 15 | ) 16 | 17 | #_(defn sing 18 | "Given a start and an optional end, returns all verses in this interval. If 19 | end is not given, the whole song from start is sung." 20 | ([start]) 21 | ([start end])) 22 | 23 | #_(defn verse 24 | "Returns the nth verse of the song." 25 | [num] 26 | (cond 27 | (= num 0) 28 | (str "No more bottles of beer on the wall, no more bottles of beer.\n" 29 | "Go to the store and buy some more, 99 bottles of beer on the wall.\n") 30 | 31 | (= num 1) 32 | (str "1 bottle of beer on the wall, 1 bottle of beer.\n" 33 | "Take it down and pass it around, no more bottles of beer on the wall.\n") 34 | 35 | :otherwise 36 | (str num " bottles of beer on the wall, " num " bottles of beer.\n" 37 | "Take one down and pass it around, " (dec num) " bottles of beer on the wall.\n"))) 38 | 39 | #_(verse 0) 40 | #_(verse 1) 41 | #_(verse 8) 42 | 43 | ;; generating a sequence of numbers 44 | 45 | #_(range 46 | 10) 47 | #_(range 2 11) 48 | #_(range 100 0) 49 | #_(range 100 0 -1) 50 | #_(range 99 -1 -1) 51 | 52 | #_(defn sing 53 | "Given a start and an optional end, returns all verses in this interval. If 54 | end is not given, the whole song from start is sung." 55 | ([start] 56 | (map verse (range start -1 -1))) 57 | ([start end] 58 | (map verse (range start (dec end) -1)))) 59 | 60 | ;; need to fix the condition for 2 61 | 62 | (defn verse 63 | "Returns the nth verse of the song." 64 | [num] 65 | (cond 66 | (= num 0) 67 | (str "No more bottles of beer on the wall, no more bottles of beer.\n" 68 | "Go to the store and buy some more, 99 bottles of beer on the wall.\n") 69 | 70 | (= num 1) 71 | (str "1 bottle of beer on the wall, 1 bottle of beer.\n" 72 | "Take it down and pass it around, no more bottles of beer on the wall.\n") 73 | 74 | (= num 2) 75 | (str "2 bottles of beer on the wall, 2 bottles of beer.\n" 76 | "Take one down and pass it around, 1 bottle of beer on the wall.\n") 77 | 78 | :otherwise 79 | (str num " bottles of beer on the wall, " num " bottles of beer.\n" 80 | "Take one down and pass it around, " (dec num) " bottles of beer on the wall.\n"))) 81 | 82 | (map verse (range 3 -1 -1)) 83 | ;; => ("3 bottles of beer on the wall, 3 bottles of beer.\nTake one down and pass it around, 2 bottles of beer on the wall.\n" "2 bottles of beer on the wall, 2 bottles of beer.\nTake it down and pass it around, 1 more bottle of beer on the wall.\n" "1 bottle of beer on the wall, 1 bottle of beer.\nTake it down and pass it around, no more bottles of beer on the wall.\n" "No more bottles of beer on the wall, no more bottles of beer.\nGo to the store and buy some more, 99 bottles of beer on the wall.\n") 84 | 85 | #_(sing 8 6) 86 | ;; => ("8 bottles of beer on the wall, 8 bottles of beer.\nTake one down and pass it around, 7 bottles of beer on the wall.\n" 87 | ;; "7 bottles of beer on the wall, 7 bottles of beer.\nTake one down and pass it around, 6 bottles of beer on the wall.\n" 88 | ;; "6 bottles of beer on the wall, 6 bottles of beer.\nTake one down and pass it around, 5 bottles of beer on the wall.\n") 89 | 90 | ;; a list of strings is being returned, rather than a complete string. 91 | ;; apply str will fix this 92 | 93 | (defn sing 94 | "Given a start and an optional end, returns all verses in this interval. If 95 | end is not given, the whole song from start is sung." 96 | ([start] 97 | (map verse (range start -1 -1))) 98 | ([start end] 99 | (apply str (map verse (range start (dec end) -1))))) 100 | 101 | ;; to minimise code, change the single argument branch to call the two argument branch 102 | 103 | (defn sing 104 | "Given a start and an optional end, returns all verses in this interval. If 105 | end is not given, the whole song from start is sung." 106 | ([start] 107 | (sing start -1)) 108 | ([start end] 109 | (apply str (map verse (range start (dec end) -1))))) 110 | 111 | ;; There is an extra newline character between each verse 112 | 113 | (defn sing 114 | "Given a start and an optional end, returns all verses in this interval. If 115 | end is not given, the whole song from start is sung." 116 | ([start] 117 | (sing start -1)) 118 | ([start end] 119 | (interpose "\n" (map verse (range start (dec end) -1))))) 120 | 121 | ;; the result still needs to be a string, so we use clojure.string/join 122 | 123 | (defn sing 124 | "Given a start and an optional end, returns all verses in this interval. If 125 | end is not given, the whole song from start is sung." 126 | ([start] 127 | (sing start 0)) 128 | ([start end] 129 | (clojure.string/join "\n" (map verse (range start (dec end) -1))))) 130 | 131 | ;; (clojure.string/join "," '("a" "b" "c")) 132 | 133 | ;; Rich comment block with redefined vars ignored 134 | #_{:clj-kondo/ignore [:redefined-var]} 135 | (comment 136 | 137 | ;; https://exercism.io/mentor/solutions/78229949b09243a7bfe8610a7008874f 138 | 139 | (ns beer-song) 140 | 141 | (defn verse-builder [verse] (fn [num] (str (verse num)))) 142 | 143 | (def multi-bottle-verse (verse-builder 144 | #(str % 145 | " bottles of beer on the wall, " 146 | % 147 | " bottles of beer.\n" 148 | "Take one down and pass it around, " 149 | (- % 1) 150 | (if (= % 2) 151 | " bottle" 152 | " bottles") 153 | " of beer on the wall.\n"))) 154 | 155 | (def single-bottle-verse (verse-builder 156 | #(str % 157 | " bottle of beer on the wall, " 158 | % 159 | " bottle of beer.\n" 160 | "Take it down and pass it around, no more bottles of beer on the wall.\n"))) 161 | 162 | (def zero-bottles-verse (verse-builder 163 | #(str "No more bottles of beer on the wall, no more bottles of beer.\n" 164 | "Go to the store and buy some more, " 165 | (+ % 99) 166 | " bottles of beer on the wall.\n"))) 167 | 168 | (defn verse 169 | "Returns the nth verse of the song." 170 | [num] 171 | (cond 172 | (= num 0) (zero-bottles-verse num) 173 | (= num 1) (single-bottle-verse num) 174 | :else (multi-bottle-verse num))) 175 | 176 | (defn sing 177 | "Given a start and an optional end, returns all verses in this interval. If 178 | end is not given, the whole song from start is sung." 179 | ([start] 180 | (sing start 0)) 181 | ([start end] 182 | (if (= start end) 183 | (verse end) 184 | (str (verse start) 185 | "\n" 186 | (sing (- start 1) end))))) 187 | 188 | (sing 99) 189 | (verse 2) 190 | 191 | 192 | 193 | 194 | 195 | (ns beer-song) 196 | 197 | (defn verse-builder [verse] (fn[num] (str (verse num)))) 198 | 199 | (def multi-bottle-verse (verse-builder 200 | #(str % 201 | " bottles of beer on the wall, " 202 | % 203 | " bottles of beer.\n" 204 | "Take one down and pass it around, " 205 | (dec %) 206 | (if (= % 2) 207 | " bottle" 208 | " bottles") 209 | " of beer on the wall.\n" 210 | ))) 211 | 212 | (defn multi-bottle-verse2 213 | [num] 214 | (format "%d bottles of beer on the wall, %d bottles of beer.\nTake one down and pass it around, %d bottles of beer on the wall.\n" num num (dec num))) 215 | 216 | (def single-bottle-verse "1 bottle of beer on the wall, 1 bottle of beer.\nTake it down and pass it around, no more bottles of beer on the wall.\n") 217 | (def zero-bottle-verse "No more bottles of beer on the wall, no more bottles of beer.\nGo to the store and buy some more, 99 bottles of beer on the wall.\n") 218 | 219 | (defn verse 220 | [num] 221 | (case num 222 | 0 zero-bottle-verse 223 | 1 single-bottle-verse 224 | (multi-bottle-verse num))) 225 | 226 | (defn sing 227 | ([start] 228 | (sing start 0)) 229 | ([start end] 230 | (map verse (range start (dec end) -1)))) 231 | 232 | (verse 0) 233 | (verse 1) 234 | (verse 9) 235 | (sing 3 0) 236 | 237 | (map (comp clojure.string/join "\n" verse) (range 0 3))) 238 | 239 | ;; End of rich comment block 240 | 241 | ;; Rich comment block with redefined vars ignored 242 | #_{:clj-kondo/ignore [:redefined-var]} 243 | (comment 244 | 245 | (ns beer-song 246 | (:require clojure.string)) 247 | 248 | (defn verse 249 | "Returns the nth verse of the song." 250 | [num] 251 | (cond 252 | (= num 0) 253 | (str "No more bottles of beer on the wall, no more bottles of beer.\n" 254 | "Go to the store and buy some more, 99 bottles of beer on the wall.\n") 255 | (= num 1) 256 | (str "1 bottle of beer on the wall, 1 bottle of beer.\n" 257 | "Take it down and pass it around, no more bottles of beer on the wall.\n") 258 | (= num 2) 259 | (str "2 bottles of beer on the wall, 2 bottles of beer.\n" 260 | "Take one down and pass it around, 1 bottle of beer on the wall.\n") 261 | :else 262 | (str num " bottles of beer on the wall, " num " bottles of beer.\n" 263 | "Take one down and pass it around, " (dec num) " bottles of beer on the wall.\n"))) 264 | 265 | (defn sing 266 | "Given a start and an optional end, returns all verses in this interval. If 267 | end is not given, the whole song from start is sung." 268 | ([start] 269 | (sing start 0)) 270 | ([start end] 271 | (clojure.string/join "\n" (map verse (range start (dec end) -1)))))) 272 | 273 | ;; End of rich comment block 274 | -------------------------------------------------------------------------------- /spiral-matrix/src/design_journal.clj: -------------------------------------------------------------------------------- 1 | (ns design-journal) 2 | 3 | ;; The challenge 4 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 5 | ;; Given the size, return a square matrix of numbers in spiral order. 6 | 7 | ;; The matrix should be filled with natural numbers, starting from 1 in the top-left corner, increasing in an inward, clockwise spiral order, like these examples: 8 | ;; Spiral matrix of size 3 9 | 10 | ;; 1 2 3 11 | ;; 8 9 4 12 | ;; 7 6 5 13 | 14 | ;; Spiral matrix of size 4 15 | 16 | ;; 1 2 3 4 17 | ;; 12 13 14 5 18 | ;; 11 16 15 6 19 | ;; 10 9 8 7 20 | 21 | ;; Sounds easy, right :) 22 | 23 | 24 | ;; Rich comment block with redefined vars ignored 25 | #_{:clj-kondo/ignore [:redefined-var]} 26 | (comment 27 | 28 | 29 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 30 | ;; Generating a sequence of numbers 31 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 32 | ;; A matrix can be thought of as a sequence of numbers 33 | ;; printed out in a rectangle shape 34 | 35 | ;; Create a sequence of numbers from 1 to 9 36 | 37 | (range 1 10) 38 | ;; => (1 2 3 4 5 6 7 8 9) 39 | 40 | ;; to make it more general, use range as a lazy infinite sequence 41 | ;; taking only the values required 42 | (take 9 (range)) 43 | ;; => (0 1 2 3 4 5 6 7 8) 44 | 45 | (take 9 (range 1 10)) 46 | ;; => (1 2 3 4 5 6 7 8 9) 47 | 48 | ;; Or to keep it lazy we could try rest 49 | 50 | (take 9 (rest (range))) 51 | ;; => (1 2 3 4 5 6 7 8 9) 52 | 53 | 54 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 55 | ;; Reviewing the tests for the matrix structure 56 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 57 | ;; In the test code the matrices are represented by 58 | ;; a collection of sequences, with each sequence a row of the matrix 59 | 60 | 61 | ;; To make a specific grouping we could use partition 62 | 63 | (partition 3 (take 9 (rest (range)))) 64 | ;; => ((1 2 3) (4 5 6) (7 8 9)) 65 | 66 | 67 | ;; Sequence for a 3 x 3 matrix 68 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 69 | 70 | ;; The starting matrix is 71 | ;; 1 2 3 72 | ;; 4 5 6 73 | ;; 7 8 9 74 | 75 | ;; The spiral version of the matrix is 76 | ;; 1 2 3 77 | ;; 8 9 4 78 | ;; 7 6 5 79 | 80 | ;; Which gives a sequence of 81 | [1 2 3 8 9 4 7 6 5] 82 | 83 | 84 | ;; Logic for a 3 x 3 matrix 85 | ;; Take the first row as it is 86 | ;; second row is the last two digits of the last row, plus the first digit from the second row 87 | ;; third row is the first digit from the last row, plus the last two digits of the second row reversed 88 | 89 | 90 | ;; A function that takes a number and returns a sequence of sequences 91 | ;; that should represent a matrix 92 | 93 | (defn spiral 94 | [size] 95 | (let [sequence (partition size (take (* size size) (rest (range))))] 96 | sequence)) 97 | 98 | (spiral 1) 99 | ;; This passes the first two tests, however, the third test fails 100 | ;; as the numbers now need to be generated in reverse order 101 | 102 | 103 | ;; ((1 2) (4 3)) 104 | 105 | 106 | (defn spiral 107 | [size] 108 | (let [sequence (partition size (take (* size size) (rest (range))))] 109 | (list 110 | (first sequence) 111 | (reverse (second sequence))))) 112 | 113 | 114 | (spiral 2) 115 | ;; => ((1 2) (4 3)) 116 | 117 | ;; So the third test should now pass 118 | 119 | (spiral 1) 120 | ;; => ((1) ()) 121 | 122 | 123 | ;; This approach looks like its heading for loop recur or maybe recursive function 124 | 125 | ;; Lets try thinking laterally 126 | 127 | 128 | 129 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 130 | ;; Thinking laterally 131 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 132 | 133 | ;; For a 3x3 matrix we have the following rows. 134 | ;; 1 2 3 135 | ;; 4 5 6 136 | ;; 7 8 9 137 | 138 | ;; The spiral version of the matrix is 139 | ;; 1 2 3 140 | ;; 8 9 4 141 | ;; 7 6 5 142 | 143 | ;; Which gives a sequence of 144 | [1 2 3 8 9 4 7 6 5] 145 | 146 | ;; Looking at the relative positions of the numbers 147 | 148 | ;; 1 goes into position 1 149 | ;; 2 goes into position 2 150 | ;; 3 goes into position 3 151 | ;; 4 goes into position 6 152 | ;; 5 goes into position 9 153 | ;; 6 goes into position 8 154 | ;; 7 goes into position 6 155 | ;; 8 goes into position 3 156 | ;; 9 goes into position 4 157 | 158 | ;; So if we can generate the following sequence 159 | ;; 1 2 3 6 9 8 6 3 4 160 | 161 | ;; then we can map them to incremental numbers in the matrix 162 | ;; and get an indexed set of values that can generate the spiral from. 163 | 164 | ;; 1 2 3 6 9 8 6 3 4 ;; location on grid 165 | ;; 1 2 3 4 5 6 7 8 9 ;; value at location 166 | 167 | 168 | 169 | ;; Creating mathematical pattern for generating the matrix 170 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 171 | ;; Generate a pattern that defines the order in which 172 | ;; incremental numbers should be placed to form a spiral. 173 | ;; An index for where each consecutive number should be placed 174 | 175 | ;; Creating reverse range starting from one less than the matrix row size 176 | (range (dec 3) 0 -1) 177 | ;; => (2 1) 178 | 179 | ;; duplicate the sequence 180 | (mapcat #(repeat 2 %) (range (dec 3) 0 -1)) 181 | ;; => (2 2 1 1) 182 | 183 | ;; Add the row-size to the front 184 | (cons 3 (mapcat #(repeat 2 %) (range (dec 3) 0 -1))) 185 | ;; => (3 2 2 1 1) 186 | 187 | 188 | ;; An example with a 4x4 matrix 189 | ;; (cons 4 (mapcat #(repeat 2 %) (range (dec 4) 0 -1))) 190 | ;; => (4 3 3 2 2 1 1) 191 | 192 | 193 | ;; Create a cyclical pattern that will be used to generate numbers 194 | ;; using a fairly simple mathematical progression (I think that is the right term) 195 | ;; using 1 size-of-row -1 (- size-of-row) 196 | ;; cycle will lazily generate an infinite sequence from this pattern 197 | ;; the pattern will be as big as the total size of the matrix 198 | (take 9 (cycle [1 3 -1 (- 3)])) 199 | ;; => (1 3 -1 -3 1 3 -1 -3 1 3) 200 | 201 | 202 | ;; Joint these two patterns together to generate a sequence 203 | (mapcat #(repeat %2 %) 204 | '(1 3 -1 -3 1 3 -1 -3 1 3) 205 | '(3 2 2 1 1) ) 206 | ;; => (1 1 1 3 3 -1 -1 -3 1) 207 | 208 | 209 | ;; combining the code 210 | (mapcat #(repeat %2 %) 211 | (cycle [1 3 -1 (- 3)]) 212 | (cons 3 (mapcat #(repeat 2 %) (range (dec 3) 0 -1)))) 213 | ;; => (1 1 1 3 3 -1 -1 -3 1) 214 | 215 | 216 | ;; Now we have the pattern that can be used to generate 217 | ;; the sequence of numbers that makes the spiral matrix 218 | 219 | 220 | ;; Generating the spiral sequence 221 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 222 | 223 | ;; Reductions is like reduce that shows its working... 224 | ;; if the above pattern is reduced with `+` then a single value is returned 225 | ;; with reductions each value of the reduce iteration is returned 226 | 227 | (reduce + '(1 1 1 3 3 -1 -1 -3 1)) 228 | ;; => 5 229 | 230 | (reductions + '(1 1 1 3 3 -1 -1 -3 1)) 231 | ;; => (1 2 3 6 9 8 7 4 5) 232 | 233 | 234 | ;; Putting the code together so far... 235 | (reductions + 236 | (mapcat #(repeat %2 %) 237 | (cycle [1 3 -1 (- 3)]) 238 | (cons 3 (mapcat #(repeat 2 %) (range (dec 3) 0 -1)))) ) 239 | 240 | 241 | ;; We now have a patter than 242 | 243 | ;; Add an incremental index to each value in the sequence making an ordered tuple 244 | (map vector (range 1 (inc (* 3 3))) '(1 2 3 6 9 8 7 4 5)) 245 | ;; => ([1 1] [2 2] [3 3] [4 6] [5 9] [6 8] [7 7] [8 4] [9 5]) 246 | 247 | 248 | ;; sort by the value in the matrix rather than the incremental index 249 | ;; to give the right shape to the spiral matrix 250 | 251 | (sort-by second '([1 1] [2 2] [3 3] [4 6] [5 9] [6 8] [7 7] [8 4] [9 5])) 252 | ;; => ([1 1] [2 2] [3 3] [8 4] [9 5] [4 6] [7 7] [6 8] [5 9]) 253 | 254 | 255 | ;; Extract all the values for the matrix to create a sequence 256 | (map first '([1 1] [2 2] [3 3] [8 4] [9 5] [4 6] [7 7] [6 8] [5 9])) 257 | ;; => (1 2 3 8 9 4 7 6 5) 258 | 259 | ;; Partition by the sequence by the size of the matrix 260 | (partition 3 '(1 2 3 8 9 4 7 6 5)) 261 | ;; => ((1 2 3) (8 9 4) (7 6 5)) 262 | 263 | ;; And we have our answer... 264 | 265 | 266 | ;; Put this into a function 267 | 268 | (defn spiral [size] 269 | (let [progression (cycle [1 size -1 (- size)])] 270 | (partition 271 | size 272 | (map 273 | first 274 | (sort-by 275 | second 276 | (map 277 | vector 278 | (range 1 (inc (* size size))) 279 | (reductions 280 | + 281 | (mapcat #(repeat %2 %) 282 | progression 283 | (cons size 284 | (mapcat #(repeat 2 %) 285 | (range (dec size) 0 -1))))))))))) 286 | 287 | 288 | (spiral 3) 289 | 290 | 291 | ;; Refactor the function with a threading macro 292 | (defn spiral [size] 293 | (let [progression (cycle [1 size -1 (- size)])] 294 | (->> (range (dec size) 0 -1) 295 | (mapcat #(repeat 2 %)) 296 | (cons size) 297 | (mapcat #(repeat %2 %) progression ) 298 | (reductions +) 299 | (map vector (range 1 (inc (* size size)))) 300 | (sort-by second) 301 | (map first) 302 | (partition size)))) 303 | 304 | (spiral 3) 305 | 306 | 307 | 308 | ) ;; End of rich comment block 309 | 310 | 311 | #_(map first) 312 | 313 | ;; (map inc [1 2 #_#_#_3 #_4 5]) 314 | 315 | ;; Rich comment block with redefined vars ignored 316 | #_{:clj-kondo/ignore [:redefined-var]} 317 | (comment 318 | 319 | ;; Recursive function - spiral-matrix 320 | 321 | (defn rotate [matrix] 322 | (apply map list (reverse matrix))) 323 | 324 | (defn spiral-matrix [size new-size start] 325 | (let [row (list (range start (+ start size)))] 326 | (if (= 1 new-size) 327 | row 328 | (concat row 329 | (rotate (spiral-matrix (dec new-size ) size (+ start size))))))) 330 | 331 | (defn spiral [size] 332 | (if (> size 0) 333 | (spiral-matrix size size 1) 334 | '())) 335 | 336 | (spiral 3) 337 | 338 | ) ;; End of rich comment block 339 | 340 | 341 | 342 | ;; Rich comment block with redefined vars ignored 343 | #_{:clj-kondo/ignore [:redefined-var]} 344 | (comment 345 | 346 | 347 | (def rotation 348 | (cycle '((0 1) (1 0) (0 -1) (-1 0)))) 349 | 350 | (defn runs [n] 351 | (->> (range n -1 -1) (partition 2 1) flatten)) 352 | 353 | (defn directions [n] 354 | (next (mapcat #(repeat %1 %2) (runs n) rotation))) 355 | 356 | (defn walk-from [indices dir] 357 | (conj indices (map + (last indices) dir))) 358 | 359 | (defn positions [n] 360 | (reduce walk-from ['(0 0)] (directions n))) 361 | 362 | (defn position-value-list->matrix [coll] 363 | (->> coll 364 | (sort-by (comp (juxt first second) second)) 365 | (partition-by (comp first last)))) 366 | 367 | (defn spiral [n] 368 | (->> (positions n) 369 | (take (* n n)) ; Make sure 0 is empty 370 | (map-indexed list) 371 | position-value-list->matrix 372 | (map #(map (comp inc first) %)))) 373 | 374 | (spiral 3) 375 | 376 | ) ;; End of rich comment block 377 | -------------------------------------------------------------------------------- /gigasecond/src/design_journal.clj: -------------------------------------------------------------------------------- 1 | ;; --------------------------------------------------------- 2 | ;; Exercisim: Gigasecond 3 | ;; 4 | ;; Given a moment, compute the moment that would be after an arbitrary number 5 | ;; of seconds has passed (one Gigasecond for this example). 6 | ;; 7 | ;; This function works is extreme cases like: 8 | ;; * Periods of any size (any positive number of seconds) 9 | ;; * Periods that include years like 1900 inside (oddly not leap year) 10 | ;; * Periods that start (or end), before (or after), the end of February in 11 | ;; years that are (or not) leap years 12 | ;; * Periods that start (or end), before (or after), the start (or end) of any 13 | ;; year 14 | ;; * Daylight savings time is not factored into the solution, as not all regions observer daylight savings 15 | ;; --------------------------------------------------------- 16 | 17 | 18 | ;; --------------------------------------------------------- 19 | (ns design-journal) 20 | ;; --------------------------------------------------------- 21 | 22 | ;; --------------------------------------------------------- 23 | ;; Rich comment block with redefined vars ignored 24 | (comment 25 | 26 | (ns design-journal 27 | [:import [java.time LocalDateTime]]) 28 | 29 | (defn from [year month day] 30 | (let [birth-day (LocalDateTime/of year month day 0 0 0) 31 | giga-second-day (.plusSeconds birth-day 1000000000)] 32 | [(.getYear giga-second-day) 33 | (.getMonthValue giga-second-day) 34 | (.getDayOfMonth giga-second-day)]))) 35 | 36 | ;; End of rich comment block 37 | ;; --------------------------------------------------------- 38 | 39 | ;; --------------------------------------------------------- 40 | ;; Low level approach (overly complex) 41 | (comment 42 | 43 | ;; Note: The number of seconds is embedded as `total-seconds` binding 44 | (defn from [y m d] 45 | ;; Steps: 46 | ;; 1) Convert time period to days 47 | ;; 2) Compute result thru 3 sections of the period (Start, Middle, End) 48 | ;; 2a) Start: Remaining days in first month of the period 49 | ;; 2b) Middle: Find last month of the period (loop through months/years) 50 | ;; 2c) End: Days in the last month of the period 51 | (letfn [;; Helper to compute boolean whether a year is leap or not 52 | (leap-year? [year] 53 | (cond 54 | (zero? (mod year 400)) true 55 | (zero? (mod year 100)) false 56 | :else (zero? (mod year 4)))) 57 | 58 | ;; Helper to compute total number of days in a year's month 59 | (days-in-month [year month] 60 | (cond 61 | (= month 2) (if (leap-year? year) 29 28) ; February 62 | (some #(= month %) [4 6 9 11]) 30 ; Abril, June, ... 63 | :else 31)) ; January, March, ... 64 | 65 | ;; Helper to compute the number of remaining days of a date's month 66 | ;; Ex.: [2000 2 27] results in 3 days because 2000 is a leap year 67 | ;; so day 27, day 28 and day 29 are 3 days in total. 68 | ;; (To obtain the total days of a particular month, set day to 1) 69 | (days-to-next-month [year month day] 70 | (+ (days-in-month year month) (- day) 1))] 71 | 72 | (let [total-seconds 1e9 ;; Gigasecond (works with any positive number) 73 | seconds-per-day 86400 ;; 24 x 60 x 60 74 | ;; Convert seconds to days 75 | total-days (int (/ total-seconds seconds-per-day))] 76 | 77 | ;; Loop thru months decrementing the the total days remaining in period 78 | (loop [;; Initialize date and total days in period 79 | year y 80 | month m 81 | day d 82 | remaining total-days] 83 | (let [;; Compute amount to jump according to distance to next month 84 | ;; First loop cycle computes from date's day (step 2a) 85 | ;; Following cycles compute from 1st of current month (step 2b) 86 | jump (days-to-next-month year month day)] 87 | ;; Out of the loop when no more room for another month 88 | (if-not (>= remaining jump) 89 | ;; Result (step 2c) 90 | [year month (+ day remaining)] 91 | ;; Update to next month and increment year when necesary 92 | (recur ;; Increment year when December jumps to January 93 | (if (zero? (mod month 12)) (inc year) year) 94 | ;; Circular month increment (December jumps to January) 95 | (inc (mod month 12)) 96 | 1 ;; 1st of month 97 | ;; Decrement the the total days remaining in period 98 | (- remaining jump))))))))) 99 | 100 | ;; End of rich comment block 101 | ;; --------------------------------------------------------- 102 | 103 | ;; --------------------------------------------------------- 104 | ;; Juxt tick library 105 | (comment 106 | 107 | (ns design-journal 108 | (:require [tick.alpha.api :as t])) 109 | 110 | (defn from [y m d] 111 | (let [birth (t/at (t/new-date y m d) "00:00") 112 | bill-secs (t/new-duration 1000000000N :seconds) 113 | final (t/+ birth bill-secs)] 114 | [(t/int (t/year final)) 115 | (t/int (t/month final)) 116 | (t/day-of-month final)]))) 117 | 118 | ;; End of rich comment block 119 | ;; --------------------------------------------------------- 120 | 121 | ;; --------------------------------------------------------- 122 | ;; Mostly "point-free" version using a Haskell-style flip: 123 | (comment 124 | 125 | (defn- flip [f] 126 | #(apply f %2 % %&)) 127 | 128 | (def from 129 | (comp (juxt (comp t/int t/year) 130 | (comp t/int t/month) 131 | t/day-of-month) 132 | (partial (flip t/+) (t/new-duration 1000000000N :seconds)) 133 | (partial (flip t/at) "00:00") 134 | t/new-date))) 135 | 136 | ;; End of rich comment block 137 | ;; --------------------------------------------------------- 138 | 139 | ;; --------------------------------------------------------- 140 | ;; Java.tim 141 | (comment 142 | 143 | (ns gigasecond 144 | (:import [java.time LocalDateTime])) 145 | 146 | (def gigasecond 1e9) 147 | 148 | (defn from [year month day] 149 | (as-> 150 | (.plusSeconds (LocalDateTime/of year month day 0 0) gigasecond) date-time 151 | [(.getYear date-time) (.getMonthValue date-time) (.getDayOfMonth date-time)]))) 152 | 153 | ;; End of rich comment block 154 | ;; --------------------------------------------------------- 155 | 156 | ;; --------------------------------------------------------- 157 | ;; Java Math library 158 | (comment 159 | 160 | (ns gigasecond) 161 | 162 | (def days_in_gigasecond (Math/floor (/ 1000000000 60 60 24))) 163 | 164 | (def short_months [4 6 9 11]) 165 | 166 | (defn leap [year] 167 | (and (zero? (mod year 4)) (or (not (zero? (mod year 100))) (zero? (mod year 400))))) 168 | 169 | (defn last-day-of-month [date] 170 | (or 171 | (and (= (date :month) 2) 172 | (if (leap (date :year)) 173 | (= (date :day) 29) 174 | (= (date :day) 28))) 175 | (and (some #{(date :month)} short_months) (= (date :day) 30)) 176 | (= (date :day) 31))) 177 | 178 | (defn add-day [date] 179 | (if (last-day-of-month date) 180 | (if (= (date :month) 12) 181 | {:year (inc (date :year)) :month 1 :day 1} 182 | {:year (date :year) :month (inc (date :month)) :day 1}) 183 | {:year (date :year) :month (date :month) :day (inc (date :day))})) 184 | 185 | (defn from [year month day] 186 | (vals (reduce (fn [current, _] (add-day current)) {:year year :month month :day day} (range days_in_gigasecond))))) 187 | 188 | ;; End of rich comment block 189 | ;; --------------------------------------------------------- 190 | 191 | ;; --------------------------------------------------------- 192 | ;; Java.time 193 | (comment 194 | 195 | (ns gigasecond 196 | (:import [java.time LocalDate])) 197 | 198 | (def days-of-gigasecond (long (/ 1000000000 86400))) 199 | 200 | (defn from [year month day] 201 | (let [giga-day (.plusDays (LocalDate/of year month day) 202 | days-of-gigasecond)] 203 | [(.getYear giga-day) 204 | (.getMonthValue giga-day) 205 | (.getDayOfMonth giga-day)]))) 206 | 207 | ;; This code does not handle days of different length. (The length can change because of introduction/end of DST, because of leap seconds, or because of changes of the timezone of a country.) 208 | 209 | ;; But to take care of this we would have to know at least the locale and the time of birth. Without knowing this information, just adding a fixed number of days seems to be okay to me. 210 | 211 | ;; End of rich comment block 212 | ;; --------------------------------------------------------- 213 | 214 | ;; --------------------------------------------------------- 215 | ;; Java Calendar 216 | (comment 217 | 218 | (ns gigasecond) 219 | 220 | ;;; Java date manipulation helpers 221 | 222 | (defn- date-for 223 | "Get a Java Date for the given y/m/d." 224 | [y m d] 225 | (java.util.GregorianCalendar. y (dec m) d)) 226 | 227 | (defn- date->ymd 228 | "Convert a Java Calendar to a y/m/d/ tuple." 229 | [date] 230 | [(.get date java.util.Calendar/YEAR) 231 | (inc (.get date java.util.Calendar/MONTH)) 232 | (.get date java.util.Calendar/DAY_OF_MONTH)]) 233 | 234 | (defn- seconds+ 235 | "Add seconds to a Java date." 236 | [date seconds] 237 | (let [new-date (.clone date)] 238 | (.add new-date java.util.Calendar/SECOND seconds) 239 | new-date)) 240 | 241 | (defn- gigasecond+ 242 | "Add a gigasecond to the given date." 243 | [date] 244 | (seconds+ date 1000000000N)) 245 | 246 | (defn from 247 | "Calculate the date one gigasecond from the given date. 248 | (Input and output dates are y/m/d tuples.)" 249 | [y m d] 250 | (-> (date-for y m d) 251 | gigasecond+ 252 | date->ymd))) 253 | 254 | ;; End of rich comment block 255 | ;; --------------------------------------------------------- 256 | 257 | ;; --------------------------------------------------------- 258 | ;; Java.util.Date 259 | (comment 260 | 261 | (ns gigasecond) 262 | 263 | (def ^:private gigamillisecond (* 1000000000 1000)) 264 | 265 | (defn- javaDate->vector 266 | "Convert a java.util.Date into a vector of year month day" 267 | [^java.util.Date date] 268 | [(+ 1900 (.getYear date)) (inc (.getMonth date)) (.getDate date)]) 269 | 270 | (defn- vector->javaDate 271 | "Convert a vector of year month day into a java.util.Date" 272 | ^java.util.Date [[y m d]] 273 | (java.util.Date. (- y 1900) (dec m) d)) 274 | 275 | (defn from 276 | "Get the date of 1 gigasecond from given date" 277 | [y m d] 278 | (-> [y m d] 279 | (vector->javaDate) 280 | (.getTime) 281 | (+ gigamillisecond) 282 | ; cast milliseconds to a long so the java.util.Date. 283 | ; constructor can be resolved. 284 | (long) 285 | (java.util.Date.) 286 | (javaDate->vector)))) 287 | 288 | ;; End of rich comment block 289 | ;; --------------------------------------------------------- 290 | 291 | ;; Alternate solutions 292 | ;; https://exercism.io/tracks/clojure/exercises/gigasecond/solutions/acccc3df02bf4d01b7f698e582d375b4 293 | --------------------------------------------------------------------------------