Friday, December 9, 2011

Amit Rathore's Introduction to Clojure: Week 1

I'm currently taking the Introduction to Clojure class from CodeLesson.com taught by Amit Rathore, author of Clojure In Action. (You may be able to get a 40% discount on the book if you use "clojure40" as the coupon code when purchasing through Manning, I'm not sure how long this offer is good for.) We are using the book for our coursework material along with Amit's quizzes and exercises. There are 14 students in the class, 9 from the US and the others are from Germany, Spain,  Ireland and the UK.

For the first week we've read through chapters 1 - 3 of Clojure in Action, taken 3 quizzes and have been given, what has been for me a challenging exercise. The class site uses moodle, unfortunately there has not been any audio/video presentations yet. I'm still hoping.

This week's exercise gives us a string of inputs consisting of a matrix size, [x y] coordinates and heading of an object, along with a series of left and right turns and forward movements for the object. The end goal is to have the output reveal the proper position of the object in the matrix. For extra credit we are encouraged to add collision and boundary detection, which I've not done. Credit goes to my classmate John Vieten for showing me how this was done. I copied John's design and re-implemented based on that, although I had to look back at his code from time to time as I'm still having difficulties thinking functionally. Also check out Amit's version of the solution.

;; had some frustrations with things needing to be (str)'ed
;; thus all the debug statements...

(def left-bearing-lookup {"N" "W", "W" "S", "S" "E", "E" "N"})
(def right-bearing-lookup {"N" "E", "E" "S", "S" "W", "W" "N"})

(defn turn-left [bearing] 
  (do
    ;(println "LEFT:" bearing (left-bearing-lookup bearing))
    (left-bearing-lookup bearing)))

(defn turn-right [bearing] 
  (do  
    ;(println "RIGHT:" (right-bearing-lookup bearing))
    (right-bearing-lookup bearing)))

(defn move-rover [matrix]
  (let [bearing (:bearing matrix)
        x (:x matrix)
        y (:y matrix)]
    ;(println "MOVE-ROVER:" matrix bearing x y)
    (cond
      (= bearing "N") (assoc matrix :y (inc y)) 
      (= bearing "S") (assoc matrix :y (dec y)) 
      (= bearing "W") (assoc matrix :x (dec x)) 
      (= bearing "E") (assoc matrix :x (inc x))))) 
      
(defn parse-moves [matrix move]
  ;(println "PARSE-MOVES-entry:" move)
  (cond 
    (= move \L) (assoc matrix :bearing (turn-left (:bearing matrix)))
    (= move \R) (assoc matrix :bearing (turn-right (:bearing matrix)))
    (= move \M) (move-rover matrix)))

(defn parse-rovers [matrix [[x _ y _ bearing] moves]]
  (let [x (Integer/parseInt (str x))
        y (Integer/parseInt (str y))
        bearing (str bearing)
        matrix (assoc matrix :x x :y y :bearing bearing)
        new-matrix (reduce parse-moves matrix moves)
        ]
    (do
      ;(println "BEARING:"(:bearing matrix))
      ;(println "ORIG:"matrix)
      ;(println "NEW:"new-matrix)
      (println "[" (:x new-matrix) (:y new-matrix) (:bearing new-matrix)  "]"))))

(defn process-lines [rest-lines]
  (let [rover (take 2 rest-lines)
        rover-array rover]
    (if-not (= rover [])
      (cons rover-array (process-lines (nthnext rest-lines 2))))))

(defn run-rovers [input]
  (let [lines (.split input "\n")
        [x y] (.split (first lines) " ")
        x (Integer/parseInt x)
        y (Integer/parseInt y)
        rest-lines (rest lines)
        ;;reduce is the bomb!
        matrix (reduce (fn [mymap keyvec] (assoc mymap keyvec :bearing)) 
                      {} 
                      (for [x (range 1 (+ x 1)) 
                            y (range 1 (+ y 1))] 
                        [x y] ))]
    ;(println matrix)
    ;(println rest-lines)
    ;(println (process-lines rest-lines))
    (reduce parse-rovers matrix (process-lines rest-lines))))

(run-rovers "5 5\n1 2 N\nLMLMLMLMM\n3 3 E\nMMRMMRMRRM")

Mostly for my own review, here is a brief overview of the first 3 chapters, although the actual chapters cover  A LOT more than what I glance over. While you'll need to check out the Clojure in Action book yourself to experience the expansiveness and ease of  the material presented, I highlight some of the topics, which I sometimes hadn't quite understood from reading other books. I've added links to ClojureDocs where applicable.

Chapter 1: This chapter covered a general overview of the language and its capabilities diving into concepts here and there that would later be highlighted. I really enjoyed the breakdown of the Clojure runtime versus the stages of a typical language processor and the chart showing Clojure compared with other languages, which I've taken the liberty of including:

Figure 1.4 from Clojure in Action (pg. 26)

Chapter 2: Covers the whirlwind language tour. Similar in content and style to chapter 2 "Drinking from the Clojure firehose" from the Joy of Clojure book, this chapter familiarizes the reader with the basics of the language and introduces idiomatic ways of doing things using special, forms, functions and macros.

loop: (loop bindings & body)

Much like let the loop/recur form is used by specify binding within the loop function call which are then replaced by the recur call. This operation does not consume the stack but recur can only be used in the tail position, which simply means it has to be the last statement in the loop/recur form.

(defn factorial-loop [n]
  (loop [current n factorial 1]
    (if (= current 1)
      factorial 
      (recur (dec current) (* factorial current)))))

doseq, dotimes

These forms take a 2 term vector, the first is a new symbol (variable), which is then sequentially bound to each element in the the sequence from the second term. Doseq works with a function and dotimes works with a number as the second term.

map

Takes a function and a sequence of data which is then applied to that function returning a new sequence. If you give it multiple functions you have to have an equal number of sequences.

filter

Accepts a predicate, which is something that returns true or false, and a sequence which then only returns those elements that return true for the predicate.

reduce

A function which which accepts a function that expects 2 terms and a sequence of elements. The first two elements are pulled off and processed by the function, then the result of this call plus the next function are called on the function until the sequence has been gone through.

(defn factorial-reduce [n]
  (let [num (range 1 (+ n 1))]
    (reduce * num)))

for: (for seq-expr body-expr)

Used for list comprehension and can be further refined using :let, :when, and :while keywords.

(defn matrix [x y]
     (for [x (range 1 (+ 1 x)) y (range 1 (+ 1 y))] [x y]))

Which results in:

=> (matrix 3 3)
([1 1] [1 2] [1 3] [2 1] [2 2] [2 3] [3 1] [3 2] [3 3])

thread-first ->, thread-last ->> 

Macros to make nested function definitions easier to read.

assoc-in, get-in, update-in

Functions which make it easy to dig into nested maps.

Chapter 3: Dives into functions, which I'll cover a bit and scope and destructuring, which I won't get into. I found the full structure of the defn macro quite helpful:

(defn function-name doc-string? attr-map? [parameter-list]
  conditions-map?
    (expressions))

The conditions map can take :pre and :post keywords which are applied before and after the function is executed.

arity

This is the number of arguments a function can take. A function which takes variable arity is said to be variadic and uses the form (defn function-name [& nums] (apply + nums)). You can precede the & with a known number of symbols if it has to have a certain minimum arguments, the remainder will be returned as a single list.

overloading

In order to overload a function you supply multiple arity parameters with bodies within a single function.

trampoline

This function allows you to call mutually recursive functions which do not eat up the stack. An example would be one recursive function calling another and the other calling the first. You have to use (declare function-name) for the second function, which is used in the first function, since it won't exist the first rime through.

time

Wrap your function calls with time to get the elapsed time taken for execution.

memoize

Wrap your function with a call to memoize, which will cache the results upon the second call. Can be used to speed things up in certain scenarios.

lexical closures
(the above link takes you Andrew Buntine's blog, which has a nice, simple explanation of the concept)

These are forms that enclose over free variables, which means the variable has no binding within the lexical scope of the form.
(defn make-scale [scale]
  (fn [x]
    (* x scale)))

(def make-percent (make-scale 100))

(make-percent 1.20)

Which results in:
=> (make-percent 1.20)
120.0

metadata

Can be used much like annotations.

No comments:

Post a Comment