Parts 1 through 6 of this series can be found here, here, here, here, here and here.
Alright, so I kind of just remembered that there was still one chapter left to go – so here it is! Until _why decides to come out of hiding and write an 8th chapter (whenever that happens, if ever…), this will be the final installment of my translation of _why’s (poignant) guide to ruby into Clojure.
This is kind of a weird chapter in that the whole thing is hand-drawn (by one of the foxes, presumably), making the actual code examples a little hard to find. I feel like I’ve hit all the important bits that would constitute “examples.” This is kind of a short chapter in terms of code content, but it’s a fun one nonetheless. We’ve got a very brief example of how you might build a text adventure game using classes and metaprogramming, a function that takes a string and turns it upside-down, an example of overloading an arithmetic operator to work on arrays, and an interesting encryption algorithm.
Chapter 7
; ex. 1:
(defprotocol Actions
(look [room])
(stab [room])
(sleep [room]))
(defrecord BanquetRoom []
Actions
(look [_] "Red with mirrors")
(stab [_] "The room screams!")
(sleep [_] "Ahh, you slept on food"))
(print "Banquet Room. Do what?")
(let [what-to-do (clojure.string/trim (read-line))]
(eval `(~(symbol what-to-do) (BanquetRoom.))))
; ex. 2:
(defn umop-apisdn [txt]
(let [in "ahbmfnjpdrutwqye"
out (reverse in)
in-out (zipmap in out)]
(apply str (reverse (map #(in-out % %) txt)))))
; ex. 3:
(def divide /)
(defmulti / (fn [arg1 arg2 & more] (and (coll? arg1) (number? arg2))))
(defmethod / false [& args] (apply divide args))
(defmethod / true [coll n] (partition n n nil coll))
(/ [:mad :bone :and :his :buried :head] 3)
(/ (range 1 11) 5)
I noticed in the “Decody” example that _why made a small, but confusing mistake when commenting his code: In the lines of code referring to 2 letters on the same row, he commented “on the same column,” and vice versa. I have corrected this for the sake of clarity (and to avoid confusing myself!).
; ex. 4:
(defprotocol Encoding
(locate [decody letter])
(at [decody row col])
(encode [decody txt]))
(defrecord DeCody [key]
Encoding
(locate [_ letter]
(let [i (.indexOf key (str letter))]
[(int (/ i 5)) (rem i 5)]))
(at [_ row col]
(get key (+ (* row 5) col)))
(encode [decody txt]
(let [pairs (->> (clojure.string/replace txt #"[^a-z]" "")
(#(clojure.string/replace % "j" "i"))
(partition 2 2 nil)
(mapcat (fn [[ltr1 ltr2]]
(cond
(nil? ltr2) [[ltr1 \x]]
(= ltr1 ltr2) (repeat 2 [ltr1 \q])
:else [[ltr1 ltr2]]))))]
(apply str
(mapcat (fn [pair]
(let [[l1 l2 :as ls] (map #(locate decody %) pair)]
(cond
(= (first l1) (first l2)) ; on the same row
(letfn [(move-right [[row col]]
[row (rem (inc col) 5)])]
(map #(apply at decody (move-right %)) ls))
(= (last l1) (last l2)) ; on the same column
(letfn [(move-down [[row col]]
[(rem (inc row) 5) col])]
(map #(apply at decody (move-down %)) ls))
:else ; normal swap
[(at decody (first l1) (last l2))
(at decody (first l2) (last l1))])))
pairs)))))
(defn decody [key]
"Takes a string with arbitrary whitespace and removes all whitespace
and other non-letter characters, returning a DeCody instance with
the resulting string as a key (25 letters arranged in a 5x5 grid)."
(DeCody. (clojure.string/replace key #"[^a-z]" "")))
(def dc (decody "k x z p w
i g t f a
h o s e n
y m b r c
u l q d v"))
; example usage:
(encode dc "message to be encoded")