This is the Clojure version of the exercises in chapter 7 of David Touretzky’s book ‘Common Lisp: A Gentle Introduction to Symbolic Computation’. Why this book? Why chapter 7, why not start at the beginning? I’ll discuss the motivation for this series in another blog post, so let us dive into the exercises now.
According to Touretzky, applicative programming is one of the three programming styles besides recursion and iteration. Applicative operators like map take another function as input, applying it to a sequence of data. These operators are higher-order functions, one of the concepts of functional programming.
Exercise 7.1 makes us write a function that adds one to its input. Applying this function to mapcar adds one to each element in a list.
Exercise 7.2 defines a table with some user data. Since we are interested in each user’s social security number, applying third to mapcar on this table will do the job.
Exercise 7.3 applies zerop to each element of a list. The result is a list of t’s and nil’s (according to the given input).
Exercise 7.4 lets us define a simple predicate (greater-than-5-p) which is then applied to mapcar on a list of numbers.
This is the Lisp code of exercises 7.1 to 7.4.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
;;; ex 7.1 (defun add1 (n) (+ 1 n)) CL-USER> (mapcar #'add1 '(1 3 5 7 9)) ; (2 4 6 8 10) ;;; ex 7.2 (defparameter daily-planet '((olsen jimmy 123-76-3213 cub-reporter) (kent clark 432-24-4268 reporter) (lane lois 034-42-3467 reporter) (white perry 423-52-7889 editor))) CL-USER> (mapcar #'third daily-planet) ; (|123-76-3213| |432-24-4268| |034-42-3467| |423-52-7889|) ;;; ex 7.3 CL-USER> (mapcar #'zerop '(2 0 3 4 0 -5 -6)) ; (NIL T NIL NIL T NIL NIL) ;;; ex 7.4 (defun greater-than-5-p (n) (> n 5)) CL-USER> (mapcar #'greater-than-5-p '(1 2 3 4 5 6 7 8 9)) ; (NIL NIL NIL NIL NIL T T T T) |
And this is the Clojure version of the same exercises:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
;;; ex 7.1 (defn add1 [n] (+ 1 n)) user> (map add1 '(1 3 5 7 9)) ; (2 4 6 8 10) ;;; ex 7.2 (def daily-planet '[{:nn olsen :pn jimmy :ssn "123-76-3213" :job cub-reporter} {:nn kent :pn clark :ssn "432-24-4268" :job reporter} {:nn lane :pn lois :ssn "034-42-3467" :job reporter} {:nn white :pn perry :ssn "423-52-7889" :job editor}]) user> (map :ssn daily-planet) ; ("123-76-3213" "432-24-4268" "034-42-3467" "423-52-7889") ;;; ex 7.3 user> (map zero? '(2 0 3 4 0 -5 -6)) ; (false true false false true false false) ;;; ex 7.4 (defn greater-than-5-p [n] (> n 5)) user> (map greater-than-5-p '(1 2 3 4 5 6 7 8 9)) ; (false false false false false true true true true) |
Note the differences: apart from Clojure-typical syntax like “[]” for parameters, I also changed the structure of data like ‘daily-planet’. As the importance of lists has diminished a bit in Clojure, it’s often more useful to put structured data into maps, or here: a vector of maps.
This allows a rather concise syntax when accessing such data. Filtering out all social security numbers in ‘daily-planet’ can be done in a one-liner. For example, reading a peculiar value from a hash can be done using ‘get’:
1 2 |
(get '{:a 1 :b 2 :c 3 } :b) ; 2 |
Or you can use the keyword, instead. It works like a function:
1 2 |
user> (:b '{:a 1 :b 2 :c 3}) ; 2 |
This is the same mechanism like in the ‘daily-planet’ example. The difference is, we have several hashes within a vector. Solving this is simple: map over ‘daily-planet’ and apply the implicit ‘get’ to it.
1 2 |
user> (map #(get % :ssn) daily-planet) ; ("123-76-3213" "432-24-4268" "034-42-3467" "423-52-7889") |
This more verbose and Lisp-like way using a lambda function is equivalent:
1 2 3 4 |
user> (map (fn [record] (get record :ssn)) daily-planet) ; ("123-76-3213" "432-24-4268" "034-42-3467" "423-52-7889") |
To be continued.