Monday 29 December 2008

Clojure short-hand

After reading through some Clojure code and talking to people on IRC, I found a few ways of making code a bit more concise.

#(+ %1 %2) is short hand for (fn [x y] (+ x y)). For example, this is valid:

user> (map #(+ %1 %2) (range 0 4 ) (range 0 4))
(0 2 4 6)

Documentation for this is squirreled away on the Clojure Reader page.

Another useful function I didn't know was into which allows you to create a new data structure from an existing one. When you create literal sets, the reader creates hash types (i.e. unordered).

user> #{1 2 3 4 5 6 7 98 100}
#{1 2 98 3 4 100 5 6 7} ;; look the ordering has changed.

Using into, I can change that:

user> (into (sorted-set) #{1 2 3 4 5 6 7 98 100})
#{1 2 3 4 5 6 7 98 100}

Going back to my sort app, based on the Snake game code previously mentioned, it seems to get a drawing panel I should subclass a JPanel and override the paint method to get a canvas to draw on.

(def maxval 100)

(def model (take 100 (repeatedly (fn [] (rand-int maxval)))))

(def canvas (proxy [JPanel ActionListener] []
(paintComponent [g]
(proxy-super paintComponent g)
(.setColor g Color/RED)
(let [width (.getWidth this) height (.getHeight this) barHeight (/ height (inc (count model))) barWidthPerVal (/ width maxval)]
(prn width height)
(doseq [val (into (sorted-map) (zipmap (range 0 (count model)) model))]
(let [y (int (* (first val) barHeight)) barWidth (int (* (second val) barWidthPerVal))]
(.fillRect g 0 y barWidth barHeight)))))
(actionPerformed [e] (prn "Doing something"))))

this is still a keyword in Clojure, which is something that took me a while to work out, I guess I was hoping that I could do (.getHeight) and the this would be explicit?

proxy (as I've briefly mentioned before) allows you to override methods and implement interfaces. In this case I've overridden the painComponent method of JPanel and replaced it with some gubbins to draw the list I'm trying to sort.

I've also overridden the actionPerformed method which is where I'll mutate the model with one iteration of the chosen sort method. I've decided (again based on the Snake Clojure code) to use a Swing Timer to fire events to signal when to redraw.

On a side note, encapsulating an object behind functions is nice and simple. In the style of SICP we use a local let expression to hide x from the outside world.

(let [x (Timer. 1000 canvas)]
(defn stop-timer [] (.stop x))
(defn start-timer [] (.start x))
(defn is-running [] (.isRunning x)))

Now all I need to write in my daft demo app is a way of generating all the intermediate steps for sorting algorithms. For my implementation of bubble-sort this is simple (it generates a list of the intermediate bits and pieces), but for quick sort it's little bit harder.

No comments:

Post a Comment