Skip to content

Instantly share code, notes, and snippets.

@ddellacosta
Last active August 29, 2015 14:10
Show Gist options
  • Save ddellacosta/ba7e03951ba1bafd3ec9 to your computer and use it in GitHub Desktop.
Save ddellacosta/ba7e03951ba1bafd3ec9 to your computer and use it in GitHub Desktop.
Diamond kata -- done via "RDD" (http://blog.jayfields.com/2014/01/repl-driven-development.html) vs. TDD
(ns diamond.core)
(def alphabet "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
(defn append-end
"Takes a diamond string and appends the reverse of the string minus
first char, so i.e. ---A becomes ---A---, D--- becomes D-----D, etc."
[s]
(str s (apply str (reverse (butlast s)))))
(defn diamond-start
"Using length len to determine length of string and idx to determine inverse position
of alpha char, creates the beginning of the diamond string, like
---A, --B-, -C--, D---, etc."
[len idx alpha]
(str (apply str (take (- (dec len) idx) (repeat "-")))
(get alpha idx)
(apply str (take idx (repeat "-")))))
(defn diamond-str-fn
"Returns closure over string length len which calculates correct diamond string
per reduce iteration and appends to line sequence:
['---A---' '--B-B--' (...etc.) ]"
[len alpha]
(fn [lines idx] (->> (diamond-start len idx alpha) append-end (conj lines))))
(defn diamond
"Generates vector of diamond strings. Takes integer length len and alphabet string alpha."
[len alpha]
(assert (<= len 26))
(let [seqn (reduce (diamond-str-fn len alpha) [] (range 0 len))]
(concat seqn (reverse (butlast seqn)))))
(defn print-diamond
"Simple utility to print seq of diamond strings."
[dseq]
(doseq [line dseq] (println line)))
@ddellacosta
Copy link
Author

Whoops, I meant to pass the alphabet in as an argument to diamond just to illustrate good practice, but was referring to the ns alpha. Fixed in rev. 3. Also made my fn documentation more thorough to be consistent with what I said here.

@ddellacosta
Copy link
Author

Haskell version here for fun.

Edit: a few comments based on observations comparing the Haskell and Clojure versions.

  • the Haskell version follows the flow of data structures in a more coherent way. That is to say, it has a function which wraps up the recursive function handling the list of diamond strings, then a function handling individual strings. The algorithm is roughly the same but the structure that felt natural for the Haskell version ends up being more tied to the types, predictably. This seems to make it easier to reason about, and more concise while still quite comprehensible, in my opinion.

This could simply be because I structured the Clojure version above poorly, but I realize that in some cases I tended to split off functions more when they became hard to read vs. when it seemed logically meaningful to do so.

It's also fair to point out that I spent more time re-factoring the Clojure version, and had a better idea of the algorithm I would use before I wrote the Haskell version.

  • thinking about my criticism of Philip's solution in how it mixes up the side-effecting output code with the diamond data-structure-generating code, it's more obvious that one would keep the side-effecting code separate in the Haskell version, simply because of the types involved (I think, or I'm just so used to it it seems more obvious to me).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment