Monday, September 28, 2015

Kata: Integer Ranges in Clojure

I just did the Integer Ranges kata in Clojure.

These are the tests using Midje:

(ns integer-ranges.core-test
(:use midje.sweet)
(:use [integer-ranges.core]))
(facts
"about integer-ranges"
(fact
"it knows which numbers an interval includes"
(includes? "[2, 5]" "{2,3,4,5}") => true
(includes? "[2, 5]" "{2,-1}") => false
(includes? "[2, 5)" "{5}") => false
(includes? "(2, 5]" "{2}") => false
(includes? "(2, 5)" "{2}") => false
(includes? "(2, 5)" "{5}") => false)
(fact
"it tells all numbers an interval includes"
(all-numbers "[2,5]") => [2 3 4 5]
(all-numbers "[1,5]") => [1 2 3 4 5]
(all-numbers "(1,5]") => [2 3 4 5]
(all-numbers "(1,5)") => [2 3 4])
(fact
"it knows when an interval contains another interval"
(contains-range? "[2,10)" "[2,5]") => true
(contains-range? "(2,10]" "[2,5]") => false
(contains-range? "[2,4]" "[2,5]") => false
(contains-range? "[2,4]" "[2,5)") => true)
(fact
"it knows an interval's end points"
(end-points "[3,8]") => [3 8])
(fact
"it knows when two intervals overlap"
(overlaps? "[2,10)" "[9,10)") => true
(overlaps? "[2,10)" "[1,2)") => false
(overlaps? "[2,10)" "[10,12)") => false
(overlaps? "[2,10]" "[10,12)") => true
(overlaps? "[2,10]" "[3,5)") => true
(overlaps? "[3,5)" "[2,10]") => true
(overlaps? "[9,10)" "[2,10)") => true
(overlaps? "[1,2)" "[2,10)") => false
(overlaps? "[1,2)" "[5,10)") => false
(overlaps? "[2,10]" "(10,20)") => false
(overlaps? "[2,10]" "[10,20)") => true
(overlaps? "[2,10)" "[10,20)") => false)
(fact
"it knows if two intervals are equal or not
(being equal means that they include the same numbers)"
(equals? "[2,10)" "[9,10)") => false
(equals? "[5,8]" "[5,8]") => true
(equals? "[5,8]" "[5,9)") => true
(equals? "[4,8]" "(3,9)") => true
(equals? "(4,8]" "[5,9)") => true))
and this is the resulting code:

(ns integer-ranges.core
(:require [clojure.string :as string]))
(defn- remove-spaces [s]
(string/replace s #" " ""))
(defn- remove-brackets [descriptor]
(apply str (drop-last (drop 1 descriptor))))
(defn- numbers-descriptors [descriptor]
(-> descriptor
remove-spaces
remove-brackets
(string/split #",")))
(defn- numbers [numbers-list-descriptor]
(map #(Integer/parseInt (str %))
(numbers-descriptors numbers-list-descriptor)))
(defn- brackets [descriptor]
(let [stripped_descriptor (string/replace descriptor #" " "")]
[(first stripped_descriptor) (last stripped_descriptor)]))
(defn- closed-open-interval [descriptor]
(let [[lower upper] (numbers descriptor)
[opening-bracket closing-bracket] (brackets descriptor)]
[(if (= opening-bracket \[) lower (inc lower))
(if (= closing-bracket \]) (inc upper) upper)]))
(defn- includes-number? [[lower upper] number]
(<= lower number (dec upper)))
(defn includes? [interval-descriptor numbers-descriptor]
(every?
#(includes-number? (closed-open-interval interval-descriptor) %)
(numbers numbers-descriptor)))
(defn all-numbers [descriptor]
(apply range (closed-open-interval descriptor)))
(defn contains-range? [descriptor other-descriptor]
(let [[lower upper] (closed-open-interval descriptor)
[other-lower other-upper] (closed-open-interval other-descriptor)]
(<= lower other-lower other-upper upper)))
(def end-points numbers)
(defn overlaps? [descriptor other-descriptor]
(let [[lower upper] (closed-open-interval descriptor)
[other-lower other-upper] (closed-open-interval other-descriptor)]
(and (< lower other-upper) (> upper other-lower))))
(defn equals? [descriptor other-descriptor]
(= (closed-open-interval descriptor)
(closed-open-interval other-descriptor)))
As usual I used a mix of TDD and REPL-driven development committing after each green and each refactoring. I also committed the REPL history.

See all the commits here if you want to follow the process.

You can find all the code on GitHub.

No comments:

Post a Comment