I have decided to try to provide answers to the exercises in the best idiomatic Clojure code that I can manage, including using the clojure-contrib library where possible. I think this is the best way for me to learn to think in Clojure.
Exercise 3 wasn't too bad. It just required a little bit of lateral thinking to implement the required functionality.
; exercise 3 ; write a version of union that preserves the order ; of the original lists (defn new-union [lst1 lst2] (distinct (interleave lst1 lst2)))Exercise 4 really stumped me for a while. It was while doing this exercise that I realized how much work I had to do to think functionally, rather than imperatively. In the end I searched through the api docs to find something that implemented this functionality. I found clojure.contrib.seq-utils/frequencies and looked at the source code, which led me to the reduce function, and hence to the following solution:
; exercise 4
; define a function that takes a list
; and returns a list indicating the number
; of times each element appears, sorted
; from most common element to least
; common
(defn occurrences [lst]
(sort-by val > (reduce
(fn [counts x]
(assoc counts x (inc (get counts x 0))))
{}
lst)))
In my drive to attempt to write concise, idiomatic Clojure code, making use of libraries, I then re-wrote the function as:; better version using clojure.contrib.seq-utils/frequencies (defn better-occurences [lst] (sort-by val > (clojure.contrib.seq-utils/frequencies lst)))That was blood worth sweating. It forced me to hunt through the api documentation, and taught me how to use reduce.
By the time I reached exercise 5 I was beginning to enjoy the power of the sequence library.
; exercise 5
; Suppose the function pos+ takes a list and returns a
; list of each element plus its position
; (pos+ '(7 5 1 4))
; (7 6 3 7)
; Define this function using a) recursion, b) iteration
; c) mapcar (not sure this exists in clojure)
; a) recursion
(defn recursive-pos+
([lst] (recursive-pos+ lst 0))
([lst pos]
(if (empty? lst)
()
(cons (+ pos (first lst)) (recursive-pos+ (rest lst) (inc pos))))))
; b) iteration
(defn iterative-pos+ [lst]
(loop [result-list () working-list lst pos 0]
(if (empty? working-list)
(reverse result-list) ; yuck
(recur (cons (+ pos (first working-list)) result-list)
(rest working-list)
(inc pos)))))
; c) with map - seems to do what mapcar would do
(defn map-pos+ [lst]
(map (fn [x pos] (+ x pos)) lst (iterate inc 0)))
I just love the elegance of map-pos+.After all that, exercise 8 came quite easily:
; exercise 8
; define a function that takes a list and prints it
; in dot notation
; (showdots '(a b c))
; (A . (B . (C . NIL)))
(defn showdots [lst]
(if (empty? lst)
(str "NIL")
(str "(" (first lst) " . " (showdots (rest lst)) ")")))
As I said above, I've begun to appreciate the difference between thinking in imperative and functional terms. Working through these exercises I feel I am beginning to grok the functional approach. I have a long way to go, but the journey is proving fun.
0 comments:
Post a Comment