On Lisp -> Clojure (Chapter 2 – redux)

by fogus

Posts in this series: ch. 2, ch. 2 redux, ch. 3, ch. 4, ch. 5

; It seems that my first two chapters were well received, and garnered some positive comments and constructive criticisms (except for the commenter who talked about viagra and offshore betting). The main point made by readers was that my code was not Clojure-esque enough. Therefore, I went back to the online documentation and rethought my approach. Therefore, I present some refactored functions:

; pg. 15

;; It turns out that my original implementation of remove-if would trash the stack with very large lists. This can be verified by (on my setup at least) with (remove-if even? (range 1000000)). After digesting some tips from Chouser, I chose to rewrite the function using recur and lazy-cons. However, I have to admit that the documentation for both was … spartan.

;

(defn removeif [f lst]
  (if (seq lst) ; idiomatic
    (if (f (first lst))
      (recur f (rest lst))
      (lazycons (first lst) (removeif f (rest lst))))
    nil))

(removeif even? (range 1000000)) ; go get some coffee
;

; pg. 24

;; My initial version of triangle worked fine (with a ‘little’ caveat mentioned later). However, it was pointed out to me that performing a defn inside of a defn actually binds the inner name in the global namespace. Again, using the guidance from Chouser I was able to fix this particular problem. Additionally, the original code blew the stack at (triangle 300000) so I once again used recur to avoid that situation.

;

(defn triangle [n]
  (let [tri
        (fn [c n]
          (if (zero? n)
            c
            (recur (+ n c) ( n 1))))]
        (tri 0 n)))

(triangle 300000)
(triangle 3000000)
(triangle 30000000)
;

; pg. 19

;; The third lesson that I learned since implementing the initial chapter 2 functions was the ref element. While my original java.Array hack worked, it didn’t really teach me anything about writing Clojure code. Therefore, (thanks again to Chouser) I rewrote the make-adderb using a ref and as a result, my new function is now thread safe ;). As a side note, the documentation for refs is among the most comprehensive. Recommended reading.

;

(let [n (ref 0)]
  (defn makeadderb [m]
    (dosync (refset n m))
    (fn [x & change]
      (dosync
       (if change
         (refset n x)
         (+ (ensure n) x))))))

(def addx (makeadderb 1))
(addx 3)

(addx 100 true)
(addx 3)
;

;; I only fixed three of the functions in order to get a feel for the newly learned methodologies and constructs. I leave it as an exercise to the reader to fix the rest. :p

As always, I will post when the code is “complete”, but my progress can be followed on Github.