4Clojure Walkthrough
After completing the exercises in the Clojure Koans, I thought it would be fun to make a 2nd attempt to complete the 4clojure.com exercises. If you haven't done so already, I encourage you to go there, make yourself a free account, and try to solve the first bunch of problems.
{:deps {org.clojure/clojure {:mvn/version "1.10.1"}
;; complient is used for autocompletion
;; add your libs here (and restart the runtime to pick up changes)
compliment/compliment {:mvn/version "0.3.9"}}}
{:hello (clojure-version)}
Initial Thoughts
I'd like to see how far I can get in 30 minutes on a fresh 4clojure.com run-through ...
30 Minutes later: Yay, I've completed 21 exercises!
Alright... I'd love to share with you the exercises I've done. Also, I'd love for you to try the exercises, too! To help you do these two things, I invite you to:
click the "Remix" button in the upper right corner of this page, and then
scroll down in the remixed notebook to where it says, "Start Here".*
* Disclaimer: It is not my intent to replace (or compete with) 4 clojure with this notebook, nor can I even if I wanted to (and I don't) . I love what 4clojure has done,and continues to do. I believe that by me sharing (in some cases, modified) 4clojure exercises here alongside my answers, as I have done with the Clojure Koans, that more people can experience the joys of Clojure 🌈 If you disagree, please do not consume (ie. read or use) this content. The 4clojure code lives here: https://github.com/4clojure/4clojure
Start Here 🎌
Please note, for each exercise there are three cells.
Format
The first cell is read-only (convenient for copy-pasting & restarting if you need to)
The second cell you can edit and run
The third cell is hidden with a valid answer (don't look until you're ready!)
;; #1 Nothing but the Truth
(= __ true)
;; This cell is a static code example.
;; It won't run because there is no "play" button.
;; original exercise link: https://www.4clojure.com/problem/1
Instructions:
See the original problem statement for context: https://www.4clojure.com/problem/1
Please modify the code in the cell below by replacing the two underscores with your own code.
When you are ready to see the results of your answer, click the blue "Run Cell" button in the bottom-right corner of the cell. If you see the word "true" appear below the cell, then you got a correct answer! 🥳
;; #1 Nothing but the Truth
(= __ true)
;; Edit the code above this line, then click on the blue triangle just below.
Lastly, you're welcome to see my answer by expanding this collapsed code cell below. Hover your mouse over the "up caret" (looks like ^) and then click it to expand.
;; this cell should be hidden by default 😉
;; #1 Nothing but the Truth
(= true true)
Ready, set, go! 🚥
Now that you've seen how I do it, try the problems down below. If you get stuck, Google is your friend 😉 Also, you can ask me questions @avidrucker on Twitter.
;; #2 Simple Math
(= (- 10 (* 2 3)) __)
;; #2 Simple Math
(= (- 10 (* 2 3)) __)
;; #2 Simple Math
(= (- 10 (* 2 3)) 4)
;; #3 Intro to Strings
(= __ (.toUpperCase "hello world"))
;; #3 Intro to Strings
(= __ (.toUpperCase "hello world"))
;; #3 Intro to Strings
(= "HELLO WORLD" (.toUpperCase "hello world"))
;; #4 Intro to Lists
(= (list __) (:a :b :c))
;; #4 Intro to Lists
(= (list __) (:a :b :c))
;; #4 Intro to Lists
(= (list :a :b :c) (:a :b :c))
;; #5 Lists: conj
(= __ (conj (2 3 4) 1) (conj (3 4) 2 1))
;; #5 Lists: conj
(= __ (conj (2 3 4) 1) (conj (3 4) 2 1))
;; #5 Lists: conj
(= (1 2 3 4) (conj (2 3 4) 1) (conj (3 4) 2 1))
;; #6 Intro to Vectors
(= [__] (list :a :b :c) (vec (:a :b :c)) (vector :a :b :c))
;; #6 Intro to Vectors
(= [__] (list :a :b :c) (vec (:a :b :c)) (vector :a :b :c))
;; #6 Intro to Vectors
(= [:a :b :c] (list :a :b :c) (vec (:a :b :c)) (vector :a :b :c))
;; #7 Vectors: conj
(= __ (conj [1 2 3] 4) (conj [1 2] 3 4))
;; #7 Vectors: conj
(= __ (conj [1 2 3] 4) (conj [1 2] 3 4))
;; #7 Vectors: conj
(= __ (conj [1 2 3] 4) (conj [1 2] 3 4))
;; #8 Intro to Sets
(= __ (set (:a :a :b :c :c :c :c :d :d))
(clojure.set/union {:a :b :c} {:b :c :d}))
;; #8 Intro to Sets
(= __ (set (:a :a :b :c :c :c :c :d :d))
(clojure.set/union {:a :b :c} {:b :c :d}))
;; #8 Intro to Sets
(= {:a :b :c :d} (set (:a :a :b :c :c :c :c :d :d))
(clojure.set/union {:a :b :c} {:b :c :d}))
;; #9 Sets: conj
(= {1 2 3 4} (conj {1 4 3} __))
;; #9 Sets: conj
(= {1 2 3 4} (conj {1 4 3} __))
;; #9 Sets: conj
(= {1 2 3 4} (conj {1 4 3} 2))
;; #10 Intro to Maps
(= __ ((hash-map :a 10,:b 20,:c 30) :b)
(:b {:a 10,:b 20,:c 30}))
;; #10 Intro to Maps
(= __ ((hash-map :a 10,:b 20,:c 30) :b)
(:b {:a 10,:b 20,:c 30}))
;; #10 Intro to Maps
(= 20 ((hash-map :a 10,:b 20,:c 30) :b)
(:b {:a 10,:b 20,:c 30}))
Break-time
The first time I went through the 4clojure exercises (similar to the Koans) I found that the exercises were too difficult for me.
I had to go away for a time, living alone in a mountain cave, to meditate upon lambda calculus... Just kidding about the cave and calculus! But seriously, I did take a break from 4clojure. I tried out the Clojure Koans, exercism.io, and maria.cloud.
I hope that, by going through this Clojure notebook, you will gain further awareness of Clojure's utility as a programming language, without experiencing as steep of a learning curve as I did.
;; #11 Maps: conj
(= {:a 1,:b 2,:c 3} (conj {:a 1} __ [:c 3]))
;; #11 Maps: conj
(= {:a 1,:b 2,:c 3} (conj {:a 1} __ [:c 3]))
;; #11 Maps: conj
;; note: this does NOT work: '(:b 2)
;; note: this DOES work: [:b 2]
(= {:a 1,:b 2,:c 3} (conj {:a 1} {:b 2} [:c 3]))
;; #12 Intro to Sequences
(= __ (first (3 2 1))
(second [2 3 4])
(last (list 1 2 3)))
;; #12 Intro to Sequences
(= __ (first (3 2 1))
(second [2 3 4])
(last (list 1 2 3)))
;; #12 Intro to Sequences
(= 3 (first (3 2 1))
(second [2 3 4])
(last (list 1 2 3)))
;; #13 Sequences: rest
(= __ (rest [10 20 30 40]))
;; #13 Sequences: rest
(= __ (rest [10 20 30 40]))
;; #13 Sequences: rest
(= [20 30 40] (rest [10 20 30 40]))
;; #14 Intro to Functions
(= __ ((fn add-five [x] (+ x 5)) 3)
((fn [x] (+ x 5)) 3)
((+ % 5) 3)
((partial + 5) 3))
;; #14 Intro to Functions
(= __ ((fn add-five [x] (+ x 5)) 3)
((fn [x] (+ x 5)) 3)
((+ % 5) 3)
((partial + 5) 3))
;; #14 Intro to Functions
(= 8 ((fn add-five [x] (+ x 5)) 3)
((fn [x] (+ x 5)) 3)
((+ % 5) 3)
((partial + 5) 3))
;; #15 Double Down (modified by Avi for Nextjournal)
;; note: You will need to paste the same answer into the 4 blanks.
(and
(= (__ 2) 4)
(= (__ 3) 6)
(= (__ 11) 22)
(= (__ 7) 14))
;; #15 Double Down
(and
(= (__ 2) 4)
(= (__ 3) 6)
(= (__ 11) 22)
(= (__ 7) 14))
;; #15 Double Down
(and
(= ((* % 2) 2) 4)
(= ((* % 2) 3) 6)
(= ((* % 2) 11) 22)
(= ((* % 2) 7) 14))
;; #16 Hello World (modified by Avi for Nextjournal)
;; note: You will need to paste the same answer into the 4 blanks.
(and
(= (__ "Dave") "Hello, Dave!")
(= (__ "Jenn") "Hello, Jenn!")
(= (__ "Rhea") "Hello, Rhea!"))
;; #16 Hello World
(and
(= (__ "Dave") "Hello, Dave!")
(= (__ "Jenn") "Hello, Jenn!")
(= (__ "Rhea") "Hello, Rhea!"))
;; #16 Hello World
(and
(= ((str "Hello, " % "!") "Dave") "Hello, Dave!")
(= ((str "Hello, " % "!") "Jenn") "Hello, Jenn!")
(= ((str "Hello, " % "!") "Rhea") "Hello, Rhea!"))
;; #17 Sequences: map
(= __ (map (+ % 5) (1 2 3)))
;; #17 Sequences: map
(= __ (map (+ % 5) (1 2 3)))
;; #17 Sequences: map
(= (6 7 8) (map (+ % 5) (1 2 3)))
;; #18 Sequences: filter
(= __ (filter (> % 5) (3 4 5 6 7)))
;; #18 Sequences: filter
(= __ (filter (> % 5) (3 4 5 6 7)))
;; #18 Sequences: filter
(= (6 7) (filter (> % 5) (3 4 5 6 7)))
;; #19 Last Element (modified by Avi for Nextjournal)
;; special rule: You may NOT use the `last` function
;; note: You will need to paste the same answer into the 3 blanks.
(and
(= (__ [1 2 3 4 5]) 5)
(= (__ (5 4 3)) 3)
(= (__ ["b" "c" "d"]) "d"))
;; #19 Last Element
;; special rule: You may NOT use the `last` function
(and
(= (__ [1 2 3 4 5]) 5)
(= (__ (5 4 3)) 3)
(= (__ ["b" "c" "d"]) "d"))
;; #19 Last Element
;; special rule: You may NOT use the `last` function
(and
(= ((first (reverse %)) [1 2 3 4 5]) 5)
(= ((first (reverse %)) (5 4 3)) 3)
(= ((first (reverse %)) ["b" "c" "d"]) "d"))
;; #20 Penultimate Element (modified by Avi for Nextjournal)
;; note: You will need to paste the same answer into the 3 blanks.
(and
(= (__ (list 1 2 3 4 5)) 4)
(= (__ ["a" "b" "c"]) "b")
(= (__ [[1 2] [3 4]]) [1 2]))
;; #20 Penultimate Element
(and
(= (__ (list 1 2 3 4 5)) 4)
(= (__ ["a" "b" "c"]) "b")
(= (__ [[1 2] [3 4]]) [1 2]))
;; #20 Penultimate Element
(and
(= ((second (reverse %)) (list 1 2 3 4 5)) 4)
(= ((second (reverse %)) ["a" "b" "c"]) "b")
(= ((second (reverse %)) [[1 2] [3 4]]) [1 2]))
Home stretch! Take one last breather...
A rant on learning, and a related learning experience of mine
While the first ten 4clojure problems are pretty bite sized, and the second ten not too horribly difficult (I hope), the 21th problem really kicked my butt the first time I attempted it.
This time around, problem #21 took me close to 10 minutes because, I believe, I wasn't sure of my approach or options, and I hit a bug I couldn't resolve.
I suggest you take a few whacks at it (baseball or kendo metaphor 🤷♂️) and try to limit yourself to 15 minutes.
While many people say today that failure is an important part of learning, and that it is a good idea to fail fast, I believe that, ideally, you want to "fail gracefully"*. In other words, reward yourself for trying, and don't punish yourself if you don't get the results you want. If it's more fun for you to learn with friends, invite a friend to join you.
So, when you're ready, give this next exercise a shot. If you get it in one go, great, but if not, that's OK too!
*as it is said of error handling in software engineering
;; #21 Nth Element (modified by Avi for Nextjournal)
;; note: You will need to paste the same answer into the 4 blanks.
(and
(= (__ (4 5 6 7) 2) 6)
(= (__ [:a :b :c] 0) :a)
(= (__ [1 2 3 4] 1) 2)
(= (__ ([1 2] [3 4] [5 6]) 2) [5 6])
(= (__ ([1 2] [3 4] [5 6]) 2) [5 6]))
;; #21 Nth Element
(and
(= (__ (4 5 6 7) 2) 6)
(= (__ [:a :b :c] 0) :a)
(= (__ [1 2 3 4] 1) 2)
(= (__ ([1 2] [3 4] [5 6]) 2) [5 6])
(= (__ ([1 2] [3 4] [5 6]) 2) [5 6]))
;; #21 Nth Element
(and
(= ((last (take (inc %2) %1)) (4 5 6 7) 2) 6)
(= ((last (take (inc %2) %1)) [:a :b :c] 0) :a)
(= ((last (take (inc %2) %1)) [1 2 3 4] 1) 2)
(= ((last (take (inc %2) %1)) ([1 2] [3 4] [5 6]) 2) [5 6])
(= ((last (take (inc %2) %1)) ([1 2] [3 4] [5 6]) 2) [5 6]))
OK, you've earned a well deserved break!
How did it go? Did you solve #21? Or hit a wall? If you got stuck anywhere, what helped you get unstuck? As always, comments, questions, and suggestions for improvement on this notebook are much appreciated!
~ Avi