**Cons the magnificent** In this chapter I learned how to create lists using `cons` and recursion. The first function discussed was `rember`, which removes the first occurrence of `a` in `lat` and returns the result as a new list. As you can see from below, the function asks three questions: 1. Is `lat` null?, or empty. If so, return the empty list \`() 2. Is `(car lat)` equal to the atom we're looking to drop? If so, return the rest of the list `(cdr lat)` and effectively drop `(car lat)` from the list, implementing the requirement of "remove member". 3. If neither of the above is true, `(cons (car lat))` onto the natural recursion of `rember` with `a` and the rest of `lat`, which is `(cdr lat)`. \*Note that I am using `cones` in my listings to stay consistent with the book. ```clojure (with-test (def rember (fn [a lat] (cond (null? lat) '() (eq? (car lat) a) (cdr lat) :else (conss (car lat) (rember a (cdr lat)))))) (is (= (rember 'and '()) '())) (is (= (rember 'and '(and)) '())) (is (= (rember 'and '(bacon lettuce and tomato)) '(bacon lettuce tomato))) (is (= (rember 'mint '(lamb chops and mint jelly)) '(lamb chops and jelly))) (is (= (rember 'mint '(lamb chops and mint flavored mint jelly)) '(lamb chops and flavored mint jelly)))) ``` I have begun to always start my tests with the null-case, here it's... ```clojure (is (= (rember 'and '()) '())) ``` ...which drives out the question #1. Then I write the minimal test that drives out the main behavior without the need for recursion... ```clojure (is (= (rember 'and '(and)) '())) ``` ...which drives out the question #2 And then finally I write a test that drives out question #3 such as... ```clojure (is (= (rember 'and '(bacon lettuce and tomato)) '(bacon lettuce tomato))) ``` As the books describes in the beginning, over time, patterns emerge from this simple example. The second function I learned was `firsts` which takes the first s-expression of each nested list in the list. This time there are only two questions we ask: 1. Is the list `null?`, then return the empty list `(). This is the necessary terminal clause that will cause a recursion to stop. 2. Else,`(cons (car (car l))`onto the natural recursion of`firsts`with`(cdr l)\`. So, for example `(firsts '((a b) (c d) (e f)))` evaluates to `(a c e)`. Here, again, I started with the simplest case with `'()` for `l` and worked through the rest of the example step by step following the book. ```clojure (with-test (def firsts (fn [l] (cond (null? l) '() :else (cons (car (car l)) (firsts (cdr l)))))) (is (= (firsts '()) '())) (is (= (firsts '((apple peach pumpkin) (plum pear cherry) (grape raisin pea) (bean carrot eggplant))) '(apple plum grape bean))) (is (= (firsts '((a b) (c d) (e f))) '(a c e))) (is (= (firsts '((five plums) (four) (eleven green oranges))) '(five four eleven))) (is (= (firsts '(((five plums) four) (eleven green oranges) ((no) more))) '((five plums) eleven (no))))) ``` After that I wrote `insertR` which inserts a new s-expression to the right of an the first occurrence of an existing s-expression inside a list. The questions this function needs to ask and answer are: 1. Is `lat` null? If so, return the empty list \`(). 2. Is `(car lat)` equal to `old` and if so, `(cons old (cons new (cdr lat)))` which basically says append `new`, then `old` onto the rest. 3. Else `(cons (car lat))` onto the natural recursion of this function with `new`, `old` and `(cdr lat)`. ```clojure (with-test (def insertR (fn [new old lat] (cond (null? lat) '() (eq? (car lat) old) (cons old (cons new (cdr lat))) :else (cons (car lat) (insertR new old (cdr lat)))))) (is (= (insertR 'topping 'fudge '()) '())) (is (= (insertR 'topping 'fudge '(ice cream with fudge for dessert)) '(ice cream with fudge topping for dessert))) (is (= (insertR 'jalapeno 'and '(tacos tamales and salsa)) '(tacos tamales and jalapeno salsa))) (is (= (insertR 'e 'd '(a b c d f g d h)) '(a b c d e f g d h)))) ``` Now, the next function, `insertL` inserts a new element to the left of an existing one. The only difference here was what we do in case `(eq? (car lat) old)` namely `(cons new lat)` ```clojure (with-test (def insertL (fn [new old lat] (cond (null? lat) '() (eq? (car lat) old) (conss new lat) :else (cons (car lat) (insertL new old (cdr lat)))))) (is (= (insertL 'topping 'fudge '()) '())) (is (= (insertL 'topping 'fudge '(ice cream with fudge for dessert)) '(ice cream with topping fudge for dessert)))) ``` Now up to this point the functions only replace or insert or remove the _first occurence_ of whatever the matching function was. What about multiple occurrences and manipulations? When you look at `multimember` you'll notice that we drop the current occurrence of `a` in `lat` and recur via `(multirember a (cdr lat)` which will drop any following occurrences as well. ```clojure (with-test (def multirember (fn [a lat] (cond (null? lat) '() (eq? (car lat) a) (multirember a (cdr lat)) :else (conss (car lat) (multirember a (cdr lat)))))) (is (= (multirember 'cup '()) '())) (is (= (multirember 'cup '(coffee cup tea cup and hick cup)) '(coffee tea and hick)))) ``` This pattern persisted for the following, improved versions of `insertL`, `insertR` and `subst`: ```clojure (with-test (def multiinsertR (fn [new old lat] (cond (null? lat) '() (eq? (car lat) old) (conss old (conss new (multiinsertR new old (cdr lat)))) :else (conss (car lat) (multiinsertR new old (cdr lat)))))) (is (= (multiinsertR 'new 'old '()) '())) (is (= (multiinsertR 'new 'old '(old socks old beer)) '(old new socks old new beer)))) (with-test (def multiinsertL (fn [new old lat] (cond (null? lat) '() (eq? (car lat) old) (conss new (conss old (multiinsertL new old (cdr lat)))) :else (cons (car lat) (multiinsertL new old (cdr lat)))))) (is (= (multiinsertL 'new 'old '()) '())) (is (= (multiinsertL 'new 'old '(old socks old beer)) '(new old socks new old beer)))) (with-test (def multisubst (fn [new old lat] (cond (null? lat) '() (eq? (car lat) old) (conss new (multisubst new old (cdr lat))) :else (conss (car lat) (multisubst new old (cdr lat)))))) (is (= (multisubst 'new 'old '()) '())) (is (= (multisubst 'new 'old '(frog)) '(frog))) (is (= (multisubst 'new 'old '(old)) '(new))) (is (= (multisubst 'new 'old '(old old old)) '(new new new)))) ``` All in all I had great fun really digging into it. The question/answer style still lends itself to writing the test first and then following along with the implementation.