Alexis Rondeau

Newsfeed

Note · Friday, April 28, 2017 · 00:00

The Little Schemer in Clojure – Chapter 2 Recap

Yesterday I made it through Chapter 2 and as before I’ve enjoyed it. I have come to copy the question/answer style from the book into the tests:

First I write the application of the function under test, then the expectation such as:

(is (= (lat? '(Jack Sprat could eat no chicken fat)) true))

I understand that calling functions recursively, as done in the book, isn’t idiomatic in Clojure.

(def lat? 
    (fn [l]
      (cond (null? l) true
            (atom? (car l)) (lat? (cdr l))
            :else false)))

There’s recur for that. For now, I like to continue with calling functions regardless. The data-size is small and, since I have tests in place, once I’m through with the book, I can imagine a refactor using recur.

On the same note, I recognize that Clojure has great helper functions such as every? that I could leverage directly. For example, the above mentioned function lat? could become a one-liner such as by asking “is every expression in the list not a list itself”.

(def lat? 
  (fn [l] 
    (every? #(not (list? %))) l)))

This is much cleaner (and every? Itself is a recursive function) but in the spirit of the book, I’m going to stick with the functions as they are discussed. Again, my test-suite will allow for refactoring as needed later.

Lastly, I am getting into the habit of tiny, regular commits. I now make a commit when I have fully tested a function just before moving on to the next one. I can see even smaller commits on every red-to-green test cycle.

If you are interested, the full listing for Chapter 2 is as follows:

(ns the-little-clojurian.chapter2
  (:require [clojure.test :refer :all]
            [the-little-clojurian.chapter1 :refer :all]))

(with-test 
  (def lat? 
    (fn [l]
      (cond (null? l) true
            (atom? (car l)) (lat? (cdr l))
            :else false)))
  (testing "returns true"
    (is (= (lat? '(Jack Sprat could eat no chicken fat)) true))
    (is (= (lat? '()) true))
    (is (= (lat? '(bacon and eggs)) true)))
  (testing "returns false"
    (is (= (lat? '((Jack) Sprat could eat no chicken fat)) false))
    (is (= (lat? '(Jack (Sprat could) eat no chicken fat)) false))
    (is (= (lat? '(bacon (and eggs))) false))))

(deftest boolean-operators
  (is (= (or (null? '())
             (atom? '(d e f g)))
         true))
  (is (= (or (null? '(a b c))
             (null? '()))
         true))
  (is (= (or (null? '(a b c))
             (null? '(atom)))
         false)))

(with-test 
  (def member? 
    (fn [a lat]
      (cond (null? lat) false
            :else (or (eq? (car lat) a) 
                      (member? a (cdr lat))))))
  (testing "evaluates to true"
    (is (= (member? 'meat '(meat)) true))
    (is (= (member? 'tea '(coffee tea and milk))))
    (is (= (member? 'meat '(mashed potatoes and meat gravy)) true)))
  (testing "evaluates to false"
    (is (= (member? 'meat '()) false))
    (is (= (member? 'liver '(bagels and lox)) false))
    (is (= (member? 'poached '(fried eggs and scrambled eggs))))))