Record of experiments, readings, links, videos and other things that I find on the long road.
Registro de experimentos, lecturas, links, vídeos y otras cosas que voy encontrando en el largo camino.
Wednesday, December 31, 2014
Tuesday, December 30, 2014
Books I read (2014)
January
- Pan, educación, libertad (Ψωμί, παιδεία, ελευθερία), Petros Márkaris
- NW London, Zadie Smith
- 101 cuentos clásicos de la China, Chang Shiru, Ramiro A. Calle
- Mr. Neighborly's Humble Little Ruby Book, Jeremy McAnally
February
- Blankets, Craig Thompson
March
- Practical Object-Oriented Design in Ruby, Sandi Metz
- Modern C++ Programming with Test-Driven Development, Jeff Langr
April
- Learning PHP, MySQL & JavaScript, Robin Nixon
- Measuring the world, Daniel Kehlmann
May
- El hombre que plantaba árboles, (L'homme qui plantait des arbres), Jean Giono
- Extreme Programming Explained, Embrace Change, Kent Beck
- Adiós, Chunky Rice, (Good-bye, Chunky Rice), Craig Thompson
June
- Eloquent Ruby, Russ Olsen
- Implementing Domain-Driven Design, Vaughn Vernon
July
- JavaScript Allongé, Reginald Braithwaite
- JavaScript: The Good Parts, Douglas Crockford
August
- Programming Clojure, (2nd edition), Stuart Halloway and Aaron Bedra
- Introduction to Computing, Explorations in Language, Logic and Machines, David Evans
- JavaScript Allongé, Reginald Braithwaite (2nd time)
September
- Functional JavaScript, Introducing Functional Programming with Underscore.js, M. Fogus
- Functional Thinking, Paradigm Over Syntax, Neal Ford
- Understanding the Four Rules of Simple Design, Corey Haines
- A People's History of London, Lindsey German and John Rees
November
- Software Craftsmanship: Professionalism, Pragmatism, Pride, Sandro Mancuso
December
- Me llamo Rojo (Benim Adım Kırmızı), Orhan Pamuk (2nd time)
- Clojure Programming, Chas Emerick, Brian Carper and Christophe Grand
- The Little Schemer, Daniel P. Friedman and Matthias Felleisen
- Pan, educación, libertad (Ψωμί, παιδεία, ελευθερία), Petros Márkaris
- NW London, Zadie Smith
- 101 cuentos clásicos de la China, Chang Shiru, Ramiro A. Calle
- Mr. Neighborly's Humble Little Ruby Book, Jeremy McAnally
February
- Blankets, Craig Thompson
March
- Practical Object-Oriented Design in Ruby, Sandi Metz
- Modern C++ Programming with Test-Driven Development, Jeff Langr
April
- Learning PHP, MySQL & JavaScript, Robin Nixon
- Measuring the world, Daniel Kehlmann
May
- El hombre que plantaba árboles, (L'homme qui plantait des arbres), Jean Giono
- Extreme Programming Explained, Embrace Change, Kent Beck
- Adiós, Chunky Rice, (Good-bye, Chunky Rice), Craig Thompson
June
- Eloquent Ruby, Russ Olsen
- Implementing Domain-Driven Design, Vaughn Vernon
July
- JavaScript Allongé, Reginald Braithwaite
- JavaScript: The Good Parts, Douglas Crockford
August
- Programming Clojure, (2nd edition), Stuart Halloway and Aaron Bedra
- Introduction to Computing, Explorations in Language, Logic and Machines, David Evans
- JavaScript Allongé, Reginald Braithwaite (2nd time)
September
- Functional JavaScript, Introducing Functional Programming with Underscore.js, M. Fogus
- Functional Thinking, Paradigm Over Syntax, Neal Ford
- Understanding the Four Rules of Simple Design, Corey Haines
- A People's History of London, Lindsey German and John Rees
November
- Software Craftsmanship: Professionalism, Pragmatism, Pride, Sandro Mancuso
December
- Me llamo Rojo (Benim Adım Kırmızı), Orhan Pamuk (2nd time)
- Clojure Programming, Chas Emerick, Brian Carper and Christophe Grand
- The Little Schemer, Daniel P. Friedman and Matthias Felleisen
Sunday, December 28, 2014
Interesting Talk: "Introduction to Clojure"
I've just watched this interesting talk by Stuart Halloway:
Friday, December 26, 2014
Interesting Talk: "Domain-Driven Design with Clojure"
I've just watched this great talk by Amit Rathore:
Thursday, December 25, 2014
Interesting Talk: "Always Be Composing"
I've just watched this great talk about composition by Zach Tellman:
Tuesday, December 16, 2014
Clojure Developers Barcelona: Talk about Clojure destructuring
Today I gave a talk about destructuring in Clojure for some of the Clojure study group members.
This is the "clean version" of the code I wrote on the fly on Lightable to explain destructuring:
I had a great time and learned a lot preparing the talk.
I hope this small study group will be help to get the Clojure Developers Barcelona Group up and running again soon.
Thanks all for coming.
Update
I repeated this talk yesterday.
I updated the code in the gist shown above.
This is the "clean version" of the code I wrote on the fly on Lightable to explain destructuring:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; | |
;; Destructuring | |
;; | |
;; Destructuring is a concise syntax for declaratively pulling apart collections and | |
;; binding values contained therein as named locals within a let form. | |
;; (Clojure Programming, Chas Emerick, Brian Carper, Christophe Grand) | |
;; Since it's a facility provided by let, it can be used | |
;; in any expression that implicitly uses let (like fn, defn, loop, etc). | |
;; It comes in two flavours: | |
;; | |
;; 1. Sequential Destructuring | |
;; | |
(def v [51 25 3 [4 5] 6 7 [88 [99 11] 33]]) | |
;; 1.1 Simple examples | |
;; [51 25 3 [4 5] 6 7 [88 [99 11] 33]] | |
;; [x y ] | |
(let [[x y] v] | |
(vector x y)) | |
;; [51 25 3 [4 5] 6 7 [88 [99 11] 33]] | |
;; [x _ y ] | |
(let [[x _ y] v] | |
(vector x y)) | |
;; [51 25 3 [4 5] 6 7 [88 [99 11] 33]] | |
;; [x _ _ v1 ] | |
(let [[x _ _ v1] v] | |
(vector x v1)) | |
;; _ does not have a special semantics. | |
;; It is just a valid name for a var | |
;; that is used as a convention meaning | |
;; that you are not interested in its value. | |
(let [[x _ _ v1] v] | |
_) | |
;; 1.2 Nested examples | |
;; [51 25 3 [4 5] 6 7 [88 [99 11] 33]] | |
;; [x _ _ [_ y] ] | |
(let [[x _ _ [_ y]] v] | |
(+ x y)) | |
;; [51 25 3 [4 5] 6 7 [88 [99 11] 33]] | |
;; [_ _ _ [_ _] _ _ [_ [_ x] y]] | |
(let [[_ _ _ _ _ _ [_ [_ x] y]] v] | |
(+ x y)) | |
;; [51 25 3 [4 5 8 9 3] 6 7 [88 [99 11] 33]] | |
;; [_ _ _ [_ z] _ _ [_ [_ x] y]] | |
(let [[_ _ _ [_ z] _ _ [_ [_ x] y]] v] | |
(vector z x y)) | |
;; 1.3 Rest of values: & | |
(def values1 ["hi" "there" "koko" "moko"]) | |
;; ["hi" "there" "koko" "moko"] | |
;; [_ & the-rest ] | |
(let [[_ & the-rest] values1] | |
the-rest) | |
;; Notice that how the-rest is a sequence, regardless the original collection being a vector | |
;; ["hi" "there" "koko" "moko"] | |
;; [_ _ & the-rest ] | |
(let [[_ _ & the-rest] values1] | |
the-rest) | |
;; ["hi" "there" "koko" "moko"] | |
;; [& the-rest ] | |
(let [[& the-rest] values1] | |
the-rest) | |
(rest []) | |
(next []) | |
;; & has the same semantics as next (like rest except that (= nil (next '()))) | |
(let [[_ _ _ _ & the-rest] values1] | |
the-rest) | |
; This is how you can have varargs in functions | |
(defn +-mine [& args] | |
(apply + args)) | |
(+-mine) | |
(defn show-separated-by-commas [& args] | |
(clojure.string/join ", " args)) | |
(show-separated-by-commas 1 5 "hola") | |
;; 1.4 Retaining the original collection: :as | |
(def coll [3 5 8 9]) | |
(let [[x y & the-rest :as orig-coll] coll] | |
(show-separated-by-commas x y the-rest orig-coll)) | |
;; 1.5 Sequencial destructuring works with: | |
;; 1.5.a. Clojure lists, vectors and seqs | |
;; Lists | |
(let [[a _ c] '(1 2 3)] | |
(show-separated-by-commas a c)) | |
;; Seqs | |
(let [[a _ c] (filter odd? '(1 2 3 4 5 6 7))] | |
(show-separated-by-commas a c)) | |
;; 1.5.b. Any collection that implements java.util.List | |
(def an-array-list | |
(doto (java.util.ArrayList.) (.add 1) (.add 2) (.add 3))) | |
(let [[a _ c] an-array-list] | |
(show-separated-by-commas a c)) | |
;; 1.5.c. Java arrays | |
(def an-array (int-array [1 2 3])) | |
(let [[a _ c d] an-array] | |
(vector a c d)) | |
;; 1.5.d. Strings, which are destructured into characters | |
(let [[& characters] "koko"] | |
characters) | |
(seq "koko") | |
;; | |
;; 2. Map Destructuring | |
;; | |
(def a-map {:a 1 | |
:b 3 | |
:c [7 8 9] | |
:d {:e "koko" :f "moko"} | |
"foo" 88 | |
'g 99 | |
9 "nine" | |
[1 3] "hola"}) | |
;; 2.1 Simple examples | |
(let [{x :a y :b} a-map] | |
(- x y)) | |
;; Order is not important | |
(let [{y :b x :a} a-map] | |
(- x y)) | |
(:a a-map) | |
(:b a-map) | |
(get a-map :a) | |
(get a-map :b) | |
(let [{y "foo"} a-map] | |
y) | |
;("foo" a-map) | |
(get a-map "foo") | |
(let [{y 'g} a-map] | |
y) | |
(get a-map 'g) | |
(let [{y 9} a-map] | |
y) | |
(get a-map 9) | |
(let [{x [1 3]} a-map] | |
x) | |
;; 2.2 Nested examples | |
;; From a previous example: | |
;; (def a-map {:a 1 | |
;; :b 3 | |
;; :c [7 8 9] | |
;; :d {:e "koko" :f "moko"} | |
;; "foo" 88 | |
;; 'g 99 | |
;; 9 "nine" | |
;; [1 3] "hola"}) | |
;; 2.2.a Maps nested inside maps | |
(let [{inner-map :d} a-map] | |
inner-map) | |
(let [{{word :f} :d} a-map] | |
word) | |
;; Equivalent to using let with successive bindings: | |
(let [{inner-map :d} a-map | |
{word :f} inner-map] | |
word) | |
;; Or to using get-in: | |
(get-in a-map [:d :f]) | |
(let [{x :a {word :f} :d other-num :b} a-map] | |
(show-separated-by-commas x word other-num)) | |
;; 2.2.b Vectors nested inside maps | |
(def map-with-vector {:a "koko" :b ["hello" "two"]}) | |
(let [{[_ number] :b} map-with-vector] | |
number) | |
(let [[_ {x :b}] [1 {:a "x" :b 7}]] | |
x) | |
;; 2.3 Map destructuring works with anything that get function works with: | |
;; 2.3.a Clojure hash-maps, array-maps and records | |
; array-maps | |
(def an-array-map (array-map :a 10 :b 20)) | |
(let [{a :a b :b} an-array-map] | |
(show-separated-by-commas a b)) | |
; Records | |
(defrecord Point [x y]) | |
(def a-point (Point. 1 2)) | |
a-point | |
(let [{x :x y :y} a-point] | |
(show-separated-by-commas x y)) | |
;; 2.3.b Any collection that implements java.util.Map | |
(def a-java-hash-map | |
(doto (java.util.HashMap.) | |
(.put "a" "Hola") | |
(.put "b" "koko") | |
(.put "c" "moko"))) | |
(let [{x "a" y "c" } a-java-hash-map] | |
(show-separated-by-commas x y)) | |
;; 2.3.a Any value that is supported by the get function using indices as keys: | |
;; Clojure vectors, Strings and arrays | |
;; Vectors | |
;; From a previous example -> (def v [51 25 3 [4 5] 6 7 [88 [99 11] 33]]) | |
;; [51 25 3 [4 5] 6 7 [88 [99 11] 33]] | |
;; [_ _ _ [_ _] _ _ [_ [_ x] y]] | |
(let [[_ _ _ _ _ _ [_ [_ x] y]] v] | |
(show-separated-by-commas x y)) | |
; It can be also done with map-destructuring and the indexes: | |
(let [{{{x 1} 1 y 2} 6} v] | |
(show-separated-by-commas x y)) | |
(let [{[_ [_ x] y] 6} v] | |
(show-separated-by-commas x y)) | |
; Or using get-in | |
(get-in v [6 1 1]) | |
(get-in v [6 2]) | |
; Another example: | |
; From a previous example -> (def map-with-vector {:a "koko" :b ["hello" "two"]}) | |
(let [{[_ num] :b} map-with-vector] | |
num) | |
(get-in map-with-vector [:b 1]) | |
(let [{{num 1} :b} map-with-vector] | |
num) | |
;; Arrays | |
;; From a previous example -> (def an-array (int-array [1 2 3])) | |
(let [[a _ c] an-array] | |
(show-separated-by-commas a c)) | |
(let [{a 0 c 2} an-array] | |
(show-separated-by-commas a c)) | |
;; Strings | |
(let [[a _ c] "koko"] | |
(show-separated-by-commas a c)) | |
(let [{a 0 c 2} "koko"] | |
(show-separated-by-commas a c)) | |
;; 2.4 Retaining the original collection: :as | |
; From a previous example -> (def map-with-vector {:a "koko" :b ["hello" "two"]}) | |
(let [{a :a :as orig-map} map-with-vector] | |
(show-separated-by-commas a orig-map)) | |
;; 2.5 Default values: :or | |
; From a previous example -> (def map-with-vector {:a "koko" :b ["hello" "two"]}) | |
(let [{k :unknown a :a} map-with-vector] | |
(vector a k)) | |
(let [{k :unknown a :a | |
:or {k "default-value" a "not-going-to-appear!"}} map-with-vector] | |
(show-separated-by-commas a k)) | |
;; You can get a similar behaviour using: | |
(let [{k :unknown} map-with-vector | |
k (or k "default-value")] | |
k) | |
;; But this last code fails to distinguish false from nil | |
;; So that in this wicked example k gets incorrectly bound -> k should be false!! | |
(let [{k :unknown} {:unknown false} | |
k (or k "wrong-value!!!")] | |
k) | |
;; It works if you use :or (k is bound to false) | |
(let [{k :unknown | |
:or {k "wrong-value!!!"}} {:unknown false}] | |
k) | |
;; 2.6 Binding values to their keys names: :keys, :syms, :strs | |
;; This is very repetitive: | |
(let [{x :x y :y} {:x 1 :y 2}] | |
(show-separated-by-commas x y)) | |
;; You can write it in a more convenient way: | |
(let [{:keys [x y]} {:x 1 :y 2}] | |
(show-separated-by-commas x y)) | |
(get '(:x 1 :y 2) :x) | |
(let [{:keys [x y]} '(:x 1 :y 2)] | |
(show-separated-by-commas x y)) | |
(let [{x 0 y 1} [1 2]] | |
(show-separated-by-commas x y)) | |
(let [{:strs [x y]} {"x" 1 "y" 2}] | |
(show-separated-by-commas x y)) | |
(let [{:syms [x y]} {'x 1 'y 2}] | |
(show-separated-by-commas x y)) | |
;; Notice how the binding names have to be equal to the keys | |
;; It also works for records | |
;; From a previous example -> (defrecord Point [x y]) | |
(let [{x :x y :y} (Point. 1 2)] | |
(show-separated-by-commas x y)) | |
(let [{:keys [x y]} (Point. 1 2)] | |
(show-separated-by-commas x y)) | |
;; And for maps using strings as keys | |
(let [{:keys [x y]} {:x 1 "y" 2}] | |
(vector x y)) | |
(let [{:keys [x] :strs [y]} {:x 1 "y" 2}] | |
(vector x y)) | |
(let [{:keys [x] pepito "y"} {:x 1 "y" 2}] | |
(vector x pepito)) | |
;; And for maps using strings as keys | |
(let [{x "x" y "y"} {"x" 1 "y" 2}] | |
(show-separated-by-commas x y)) | |
(let [{:strs [x y]} {"x" 1 "y" 2}] | |
(show-separated-by-commas x y)) | |
;; And for maps using symbols as keys | |
(let [{x 'x y 'y} {'x 1 'y 2}] | |
(show-separated-by-commas x y)) | |
(let [{:syms [x y]} {'x 1 'y 2}] | |
(show-separated-by-commas x y)) | |
;; 2.7 Rest of key-value pairs: & | |
(def user-info ["Koko" 47 :address "Sesamo Street 26" :color "blue"]) | |
(let [[name age & {:keys [address color]}] user-info] | |
(show-separated-by-commas name age address color)) | |
(defn f [ [name age & {:keys [address color]}] ] | |
(show-separated-by-commas name age address color)) | |
(f user-info) | |
(defn make-tamagotchi [& {:keys [fulness tiredness] | |
:or {fulness 0 tiredness 0}}] | |
{:fulness fulness | |
:tiredness tiredness}) | |
(make-tamagotchi) | |
(make-tamagotchi :tiredness 4) | |
(make-tamagotchi :fulness 4) | |
(make-tamagotchi :fulness 4 :tiredness 8) | |
(let [{:keys [fulness tiredness] | |
:or {fulness 0 tiredness 0}} '(:fulness 4)] | |
(vector fulness tiredness)) | |
;; This can be used to have function with keyword arguments. | |
;; To learn more: | |
;; Clojure Programming, Practical Lisp for the Java World. Chas Emerick, Brian Carper, Christophe Grand | |
;; | |
;; The complete guide to Clojure destructuring -> http://blog.brunobonacci.com/2014/11/16/clojure-complete-guide-to-destructuring/ | |
;; | |
;; Pattern Matching vs. Destructuring… to the death! -> http://blog.fogus.me/2011/01/12/pattern-matching-vs-destructuring-to-the-death/ | |
;; | |
;; Clojure: Destructuring -> http://blog.jayfields.com/2010/07/clojure-destructuring.html | |
;; | |
;; Clojure’s Mini-languages -> http://blog.fogus.me/2010/03/23/clojures-mini-languages/ | |
;; | |
;; Destructuring on ClojureBridge -> https://clojurebridge.github.io/community-docs/docs/clojure/destructuring/ | |
;; | |
;; Clojure Binding Forms (Destructuring) -> http://clojure.org/special_forms#binding-forms | |
;; | |
;; Clojure Destructuring Tutorial and Cheat Sheet -> https://gist.github.com/john2x/e1dca953548bfdfb9844 | |
;; | |
;; Beautiful Clojure – Destructuring data -> https://www.laliluna.de/articles/2013/010/29/clojure-destructuring.html | |
; | |
;; Grover -> http://en.wikipedia.org/wiki/Grover |
I hope this small study group will be help to get the Clojure Developers Barcelona Group up and running again soon.
Thanks all for coming.
Update
I repeated this talk yesterday.
I updated the code in the gist shown above.
Monday, December 15, 2014
Kata: Roman Numerals in Clojure (2nd time)
I've just redone the Roman Numerals kata in Clojure following the advices about test-driving algorithms that Sandro Mancuso gave during his Crafted Code course:
I think the reason is that the first time I started refactoring too soon and that hid the duplication pattern that would have lead me to this simpler solution.
As in the previous version, I extended the kata to also convert decimal numbers over 3999.
This is the resulting code:
and these are the tests using Midje:
To document the TDD process I commited the code after every passing test and every refactor.
You can find the commits step by step here.
This time it was a bit easier to derive the algorithm only using strict TDD.
You can find all the code in this repository in GitHub.
- Grow an algorithm bit by bit
- Delay treating exceptions (in this case, because they are more complex)
- Intentionally cause duplication
- Focus on simple structures first
I think the reason is that the first time I started refactoring too soon and that hid the duplication pattern that would have lead me to this simpler solution.
As in the previous version, I extended the kata to also convert decimal numbers over 3999.
This is the resulting code:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns roman-numerals-converter.core) | |
(declare | |
to-roman-from-up-to-3999 | |
to-roman-from-over-3999) | |
(defn to-roman [decimal] | |
(if (<= decimal 3999) | |
(to-roman-from-up-to-3999 decimal) | |
(to-roman-from-over-3999 decimal))) | |
;; | |
;; Decimal numbers up to 3999 | |
;; | |
(def ^:private | |
decs-to-roms | |
[{:dec 1000 :rom "M"} | |
{:dec 900 :rom "CM"} | |
{:dec 500 :rom "D"} | |
{:dec 400 :rom "CD"} | |
{:dec 100 :rom "C"} | |
{:dec 90 :rom "XC"} | |
{:dec 50 :rom "L"} | |
{:dec 40 :rom "XL"} | |
{:dec 10 :rom "X"} | |
{:dec 9 :rom "IX"} | |
{:dec 5 :rom "V"} | |
{:dec 4 :rom "IV"} | |
{:dec 1 :rom "I"}]) | |
(defn- to-roman-from-up-to-3999 [decimal] | |
(loop [acc "" | |
decimal decimal | |
decs-to-roms decs-to-roms] | |
(if (zero? decimal) | |
acc | |
(let [dec-to-rom (first decs-to-roms)] | |
(if (>= decimal (:dec dec-to-rom)) | |
(recur (str acc (:rom dec-to-rom)) | |
(- decimal (:dec dec-to-rom)) | |
decs-to-roms) | |
(recur acc | |
decimal | |
(rest decs-to-roms))))))) | |
;; | |
;; Decimal numbers over 3999 | |
;; | |
(def ^:private join-str | |
(partial apply str)) | |
(defn- times-thousand-bar [multiple] | |
(concat (repeat (count multiple) "-") "\n")) | |
(defn- times-thousand-bars [multiple times] | |
(join-str | |
(flatten | |
(repeat times | |
(times-thousand-bar multiple))))) | |
(defn- multiple [decimal] | |
(loop [times 0 num decimal] | |
(if (<= num 3999) | |
[(to-roman-from-up-to-3999 num) times] | |
(recur (inc times) (quot num 1000))))) | |
(defn- to-roman-from-over-3999 [decimal] | |
(let [[multiple times] (multiple decimal)] | |
(str (times-thousand-bars multiple times) | |
multiple | |
(to-roman-from-up-to-3999 | |
(rem decimal (* times 1000)))))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns roman-numerals-converter.core-test | |
(:use midje.sweet) | |
(:use [roman-numerals-converter.core])) | |
(facts | |
"about Roman numerals converter" | |
(fact | |
"converts decimal numbers up to 3999 into roman numbers" | |
(to-roman 1) => "I" | |
(to-roman 2) => "II" | |
(to-roman 3) => "III" | |
(to-roman 4) => "IV" | |
(to-roman 5) => "V" | |
(to-roman 6) => "VI" | |
(to-roman 8) => "VIII" | |
(to-roman 9) => "IX" | |
(to-roman 10) => "X" | |
(to-roman 11) => "XI" | |
(to-roman 18) => "XVIII" | |
(to-roman 25) => "XXV" | |
(to-roman 33) => "XXXIII" | |
(to-roman 34) => "XXXIV" | |
(to-roman 39) => "XXXIX" | |
(to-roman 40) => "XL" | |
(to-roman 50) => "L" | |
(to-roman 90) => "XC" | |
(to-roman 100) => "C" | |
(to-roman 400) => "CD" | |
(to-roman 500) => "D" | |
(to-roman 900) => "CM" | |
(to-roman 1000) => "M" | |
(to-roman 2499) => "MMCDXCIX" | |
(to-roman 3949) => "MMMCMXLIX" | |
(to-roman 3999) => "MMMCMXCIX") | |
(fact | |
"converts decimal numbers over 3999 into roman numbers" | |
(to-roman 4000) => "--\nIV" | |
(to-roman 4001) => "--\nIVI" | |
(to-roman 30000) => "---\nXXX" | |
(to-roman 3999005) => "---------\nMMMCMXCIXV" | |
(to-roman 4000000) => "--\n--\nIV" | |
(to-roman 4000025) => "--\n--\nIVXXV")) |
This time it was a bit easier to derive the algorithm only using strict TDD.
You can find all the code in this repository in GitHub.
Friday, December 12, 2014
Kata: Studious Student in Clojure
Last week Leo Antoli facilitated a kata for Software Craftsmanship Barcelona.
We had to solve the Studious Student exercise.
Leo wanted us to experiment how useful testing is in an exercise like this one, so we divided the group in some pairs using TDD, some pairs testing after writing the code and some pairs not testing at all.
The exercise is a bit tricky as pairs that were not testing discovered at the end, whereas the ones doing TDD or testing after coding discovered early during the process.
After an iteration of one hour, we had a very interesting debate about automatic testing.
This is my solution of the exercise in Clojure:
and these are the tests using Midje:
You can see all the code in this GitHub repository.
I'd like to thank Leo for being so kind and sharing his knowledge with us.
We had to solve the Studious Student exercise.
Leo wanted us to experiment how useful testing is in an exercise like this one, so we divided the group in some pairs using TDD, some pairs testing after writing the code and some pairs not testing at all.
The exercise is a bit tricky as pairs that were not testing discovered at the end, whereas the ones doing TDD or testing after coding discovered early during the process.
After an iteration of one hour, we had a very interesting debate about automatic testing.
This is my solution of the exercise in Clojure:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns studious-student.core) | |
(def ^:private join (partial apply str)) | |
(defn lexic-shortest-concat [words-list] | |
(join (sort #(compare (str %1 %2) | |
(str %2 %1)) | |
words-list))) | |
(defn- file-lines [file] | |
(rest (clojure.string/split-lines (slurp file)))) | |
(defn- line-words [line] | |
(rest (clojure.string/split line #" "))) | |
(defn- extract-words-lists [file] | |
(map line-words (file-lines file))) | |
(defn lexic-shortest-concat-lines [file] | |
(->> | |
file | |
extract-words-lists | |
(map lexic-shortest-concat))) | |
(defn studious-student [file-in file-out] | |
(spit file-out | |
(clojure.string/join | |
"\n" | |
(lexic-shortest-concat-lines file-in)))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns studious-student.core-test | |
(:use midje.sweet) | |
(:use [studious-student.core])) | |
(facts | |
"about Studious Student exercise" | |
(facts | |
"about concatenating the words in a list of words | |
to generate the lexicographically lowest possible string" | |
(fact | |
"it works for an empty words list" | |
(lexic-shortest-concat []) => "") | |
(fact | |
"it works for trivial non-empty words lists" | |
(lexic-shortest-concat | |
["facebook" "hacker" "cup" "for" "studious" "students"]) | |
=> "cupfacebookforhackerstudentsstudious" | |
(lexic-shortest-concat | |
["k" "duz" "q" "rc" "lvraw"]) => "duzklvrawqrc" | |
(lexic-shortest-concat | |
["mybea" "zdr" "yubx" "xe" "dyroiy"]) => "dyroiymybeaxeyubxzdr" | |
(lexic-shortest-concat | |
["uiuy" "hopji" "li" "j" "dcyi"])=> "dcyihopjijliuiuy") | |
(fact | |
"it also works for non-trivial word lists" | |
(lexic-shortest-concat | |
["jibw" "ji" "jp" "bw" "jibw"]) => "bwjibwjibwjijp")) | |
(facts | |
"about concatenating the words in each line of a file | |
to generate the lexicographically lowest possible strings" | |
(fact | |
"it reads a file and concatenates the words in each line | |
to generate the lexicographically lowest possible strings" | |
(lexic-shortest-concat-lines | |
"./test/studious_student/studious_student.in") | |
=> '("cupfacebookforhackerstudentsstudious" | |
"duzklvrawqrc" | |
"dyroiymybeaxeyubxzdr" | |
"bwjibwjibwjijp" | |
"dcyihopjijliuiuy")) | |
(let [out "./test/studious_student/s.out"] | |
(fact | |
"it writes an output files with the lexicographically lowest possible strings | |
of the words in each line of a given file" | |
(do (studious-student "./test/studious_student/studious_student.in" out) | |
(clojure.string/split-lines (slurp out))) | |
=> '("cupfacebookforhackerstudentsstudious" | |
"duzklvrawqrc" | |
"dyroiymybeaxeyubxzdr" | |
"bwjibwjibwjijp" | |
"dcyihopjijliuiuy") | |
(against-background (after :facts (clojure.java.io/delete-file out)))) | |
(fact | |
"it also works for the long given input file" | |
(do (studious-student "./test/studious_student/studious_student_long.in" out) | |
(slurp out) => (slurp "./test/studious_student/studious_student_long.out")) | |
(against-background (after :facts (clojure.java.io/delete-file out))))))) |
You can see all the code in this GitHub repository.
I'd like to thank Leo for being so kind and sharing his knowledge with us.
Reading GOOS (IX)
These are the links mentioned in last two weeks conversations about the 12th and 13th chapters:
- Posts and Papers
- Talks
Saturday, December 6, 2014
Subscribe to:
Posts (Atom)