Solutions to 4Clojure Elementary Problems

In this post, I will share solutions to the elementary problems I solved on 4Clojure. You can find all the solutions on GitHub. If you have any suggestions/alternative solutions I would love to hear from you.

1. Nothing but the Truth

This is a clojure form. Enter a value which will make the form evaluate to true.

(def nothing-but-the-truth true)

[(= nothing-but-the-truth true)]

2. Simple Math

If you are not familiar with polish notation, simple arithmetic might seem confusing.

(def simple-math 4)

[(= (- 10 (* 2 3)) simple-math)]

3. Intro to Strings

Clojure strings are Java strings. This means that you can use any of the Java string methods on Clojure strings.

(def intro-to-strings "HELLO WORLD")

[(= intro-to-strings (.toUpperCase "hello world"))]

4. Intro to Lists

Lists can be constructed with either a function or a quoted form.

[(= (list :a :b :c) '(:a :b :c))]

5. Lists: conj

When operating on a list, the conj function will return a new list with one or more items "added" to the front.

(def lists-conj '(1 2 3 4))

[(= lists-conj (conj '(2 3 4) 1))
 (= lists-conj (conj '(3 4) 2 1))]

6. Intro to Vectors

Vectors can be constructed several ways. You can compare them with lists.

(def intro-to-vectors [:a :b :c])

[(= intro-to-vectors (list :a :b :c) (vec '(:a :b :c)) (vector :a :b :c))]

7. Vectors: conj

When operating on a Vector, the conj function will return a new vector with one or more items "added" to the end.

(def vectors-conj [1 2 3 4])

[(= vectors-conj (conj [1 2 3] 4))
 (= vectors-conj (conj [1 2] 3 4))]

8. Intro to Sets

Sets are collections of unique values.

(def intro-to-sets #{:a :b :c :d})

[(= intro-to-sets (set '(:a :a :b :c :c :c :c :d :d)))
 (= intro-to-sets (clojure.set/union #{:a :b :c} #{:b :c :d}))]

9. Sets: conj

When operating on a set, the conj function returns a new set with one or more keys "added".

(def sets-conj #{1 2 3 4})

[(= sets-conj (conj #{1 4 3} 2))]

10. Intro to Maps

Maps store key-value pairs. Both maps and keywords can be used as lookup functions. Commas can be used to make maps more readable, but they are not required.

(def intro-to-maps 20)

[(= intro-to-maps ((hash-map :a 10, :b 20, :c 30) :b))
 (= intro-to-maps (:b {:a 10, :b 20, :c 30}))]

11. Maps: conj

When operating on a map, the conj function returns a new map with one or more key-value pairs "added".

(def maps-conj {:a 1, :b 2, :c 3})

[(= maps-conj (conj {:a 1} {:b 2} [:c 3]))]

12. Intro to Sequences

All Clojure collections support sequencing. You can operate on sequences with functions like first, second, and last.

(def intro-to-sequences 3)

[(= intro-to-sequences (first '(3 2 1)))
 (= intro-to-sequences (second [2 3 4]))
 (= intro-to-sequences (last (list 1 2 3)))]

13. Sequences: rest

The rest function will return all the items of a sequence except the first.

(def sequences-rest [20 30 40])

[(= sequences-rest (rest [10 20 30 40]))]

14. Intro to Functions

Clojure has many different ways to create functions.

(def intro-to-functions 8)

[(= intro-to-functions ((fn add-five [x] (+ x 5)) 3))
 (= intro-to-functions ((fn [x] (+ x 5)) 3))
 (= intro-to-functions (#(+ % 5) 3))
 (= intro-to-functions ((partial + 5) 3))]

15. Double Down

Write a function which doubles a number.

(defn double-down [n]
  (* 2 n))

[(= (double-down 2) 4)
 (= (double-down 3) 6)
 (= (double-down 11) 22)
 (= (double-down 7) 14)]

16. Hello World

Write a function which returns a personalized greeting.

(defn hello-world [name]
  (str "Hello, " name "!"))

[(= (hello-world "Dave") "Hello, Dave!")
 (= (hello-world "Jenn") "Hello, Jenn!")
 (= (hello-world "Rhea") "Hello, Rhea!")]

17. Sequences: map

The map function takes two arguments: a function (f) and a sequence (s). Map returns a new sequence consisting of the result of applying f to each item of s. Do not confuse the map function with the map data structure.

(def sequences-map '(6 7 8))

[(= sequences-map (map #(+ % 5) '(1 2 3)))]

18. Sequences: filter

The filter function takes two arguments: a predicate function (f) and a sequence (s). Filter returns a new sequence consisting of all the items of s for which (f item) returns true.

[(= '(6 7) (filter #(> % 5) '(3 4 5 6 7)))]

35. Local bindings

Clojure lets you give local names to values using the special let-form.

[(= 7 (let [x 5] (+ 2 x)))
 (= 7 (let [x 3, y 10] (- y x)))
 (= 7 (let [x 21] (let [y 3] (/ x y))))]

36. Let it Be

Can you bind x, y, and z so that these are all true?

[(= 10 (let [x 7, y 3, z 1] (+ x y)))
 (= 4 (let [x 7, y 3, z 1] (+ y z)))
 (= 1 (let [x 7, y 3, z 1] z))]

37. Regular Expressions

Regex patterns are supported with a special reader macro.

(def regular-expressions "ABC")

[(= regular-expressions (apply str (re-seq #"[A-Z]+" "bA1B3Ce ")))]

52. Intro to Destructuring

Let bindings and function parameter lists support destructuring.

[(= [2 4] (let [[a b c d e] [0 1 2 3 4]] [c e]))]

57. Simple Recursion

A recursive function is a function which calls itself. This is one of the fundamental techniques used in functional programming.

(def simple-recursion '(5 4 3 2 1))

[(= simple-recursion ((fn foo [x]
                        (when (> x 0)
                          (conj (foo (dec x)) x))) 5))]

64. Intro to Reduce

Reduce takes a 2 argument function and an optional starting value. It then applies the function to the first 2 items in the sequence (or the starting value and the first element of the sequence). In the next iteration the function will be called on the previous return value and the next item from the sequence, thus reducing the entire collection to one value. Don’t worry, it’s not as complicated as it sounds.

(def intro-to-reduce +)

[(= 15 (reduce intro-to-reduce [1 2 3 4 5]))
 (=  0 (reduce intro-to-reduce []))
 (=  6 (reduce intro-to-reduce 1 [2 3]))]

68. Recurring Theme

Clojure only has one non-stack-consuming looping construct: recur. Either a function or a loop can be used as the recursion point. Either way, recur rebinds the bindings of the recursion point to the values it is passed. Recur must be called from the tail-position, and calling it elsewhere will result in an error.

(def recurring-theme [7 6 5 4 3])

[(= recurring-theme
   (loop [x 5
          result []]
     (if (> x 0)
       (recur (dec x) (conj result (+ 2 x)))
       result)))]

71. Rearranging Code: - >

The - > macro threads an expression x through a variable number of forms. First, x is inserted as the second item in the first form, making a list of it if it is not a list already. Then the first form is inserted as the second item in the second form, making a list of that form if necessary. This process continues for all the forms. Using - > can sometimes make your code more readable.

(def rearranging-code last)

[(= (rearranging-code (sort (rest (reverse [2 5 4 1 3 6]))))
    (-> [2 5 4 1 3 6] (reverse) (rest) (sort) (rearranging-code))
    5)]

72. Rearranging Code: - >>

The - >> macro threads an expression x through a variable number of forms. First, x is inserted as the last item in the first form, making a list of it if it is not a list already. Then the first form is inserted as the last item in the second form, making a list of that form if necessary. This process continues for all the forms. Using - >> can sometimes make your code more readable.

(defn rearranging-code [s]
  (reduce + s))

[(= (rearranging-code (map inc (take 3 (drop 2 [2 5 4 1 3 6]))))
   (->> [2 5 4 1 3 6] (drop 2) (take 3) (map inc) (rearranging-code))
   11)]

134. A nil key

Write a function which, given a key and map, returns true iff the map contains an entry with that key and its value is nil.

(defn a-nil-key [key map]
  (if (contains? map key)
    (= (key map) nil)
  	false))

[(true?  (a-nil-key :a {:a nil :b 2}))
 (false? (a-nil-key :b {:a nil :b 2}))
 (false? (a-nil-key :c {:a nil :b 2}))]

145. For the win

Clojure’s for macro is a tremendously versatile mechanism for producing a sequence based on some other sequence(s). It can take some time to understand how to use it properly, but that investment will be paid back with clear, concise sequence-wrangling later. With that in mind, read over these for expressions and try to see how each of them produces the same result.

(def for-the-win [1 5 9 13 17 21 25 29 33 37])

[(= for-the-win (for [x (range 40)
             :when (= 1 (rem x 4))]
         x))
 (= for-the-win (for [x (iterate #(+ 4 %) 0)
             :let [z (inc x)]
             :while (< z 40)]
         z))
 (= for-the-win (for [[x y] (partition 2 (range 20))]
         (+ x y)))]

156. Map Defaults

When retrieving values from a map, you can specify default values in case the key is not found: (= 2 (:foo {:bar 0, :baz 1} 2)) However, what if you want the map itself to contain the default values? Write a function which takes a default value and a sequence of keys and constructs a map.

(defn map-defaults [default keys]
  (zipmap keys (repeat default)))

[(= (map-defaults 0 [:a :b :c]) {:a 0 :b 0 :c 0})
 (= (map-defaults "x" [1 2 3]) {1 "x" 2 "x" 3 "x"})
 (= (map-defaults [:a :b] [:foo :bar]) {:foo [:a :b] :bar [:a :b]})]

161. Subset and Superset

Set A is a subset of set B, or equivalently B is a superset of A, if A is "contained" inside B. A and B may coincide.

(def subset-and-superset #{1 2})

[(clojure.set/superset? subset-and-superset #{2})
 (clojure.set/subset? #{1} subset-and-superset)
 (clojure.set/superset? subset-and-superset #{1 2})
 (clojure.set/subset? #{1 2} subset-and-superset)]

162. Logical falsity and truth

In Clojure, only nil and false represent the values of logical falsity in conditional tests - anything else is logical truth.

(def logical-falsity-and-truth 1)

[(= logical-falsity-and-truth (if-not false 1 0))
 (= logical-falsity-and-truth (if-not nil 1 0))
 (= logical-falsity-and-truth (if true 1 0))
 (= logical-falsity-and-truth (if [] 1 0))
 (= logical-falsity-and-truth (if [0] 1 0))
 (= logical-falsity-and-truth (if 0 1 0))
 (= logical-falsity-and-truth (if 1 1 0))]

These set of problems are pretty simple to solve which is excellent if you’re just getting started with a language. In future posts I will continue with harder problems. If you want to dive deeper into Clojure check out Daniel Higginbotham’s book: Clojure for the Brave and True.