Skip to content

Instantly share code, notes, and snippets.

@silb
Created March 20, 2012 19:47
Show Gist options
  • Save silb/2140477 to your computer and use it in GitHub Desktop.
Save silb/2140477 to your computer and use it in GitHub Desktop.
Capgemini Stavanger code dojo 2012-02-7: Integer to roman numeral in Clojure
(ns roman)
(declare test-integer-to-roman)
(def roman-numerals
["I" 1,
"IV" 4,
"V" 5,
"IX" 9,
"X" 10,
"XL" 40
"L" 50,
"XC" 90,
"C" 100,
"CD" 400,
"D" 500,
"CM" 900
"M" 1000])
(def roman-numerals-index
;Vector of [r d nidx] where r is a roman numeral, d is the decimal value of r and
;nidx is the index of the largest numeral that is smaller than r.
(let [even-count (/ (count roman-numerals) 2)]
(vec
(for [[even-idx nidx] (take even-count (map vector (iterate #(+ 2 %1) 0) (iterate inc -1)))]
[(roman-numerals even-idx) (roman-numerals (inc even-idx)) nidx]))))
(defn- next-roman [[r d nidx]]
(if (< nidx 0)
nil
(roman-numerals-index nidx)))
(defn- roman [n [r d nidx :as indexed-roman] ^StringBuilder acc]
"n decimal number to convert,
[r d nidx] entry from roman-numerals-index,
acc accumulated value of the conversion."
(let [rcount (int (/ n d)) ; The number of times the roman literal r occurs in n
unconverted (- n (* rcount d))] ; Remaining unconverted part of n
(dotimes [_ rcount] (.append acc r))
(if (= unconverted 0)
acc
(recur unconverted (next-roman indexed-roman) acc))))
(defn
#^{:test #'test-integer-to-roman}
integer-to-roman [n]
{:pre [(> n 0) (< n 4000)]}
"Convert the integer n to roman numerals."
(str (roman n (last roman-numerals-index) (StringBuilder.))))
(defn- test-integer-to-roman []
;; The symbols
(assert (= "I" (integer-to-roman 1)))
(assert (= "IV" (integer-to-roman 4)))
(assert (= "V" (integer-to-roman 5)))
(assert (= "IX" (integer-to-roman 9)))
(assert (= "X" (integer-to-roman 10)))
(assert (= "XL" (integer-to-roman 40)))
(assert (= "L" (integer-to-roman 50)))
(assert (= "XC" (integer-to-roman 90)))
(assert (= "C" (integer-to-roman 100)))
(assert (= "CD" (integer-to-roman 400)))
(assert (= "D" (integer-to-roman 500)))
(assert (= "CM" (integer-to-roman 900)))
(assert (= "M" (integer-to-roman 1000)))
;; Multiple occurances
(assert (= "II" (integer-to-roman 2)))
(assert (= "III" (integer-to-roman 3)))
;; Other rules
(assert (= "XXX" (integer-to-roman 30)))
(assert (= "XIX" (integer-to-roman 19)))
(assert (= "XLIX" (integer-to-roman 49)))
(assert (= "XCIX" (integer-to-roman 99)))
(assert (= "XCV" (integer-to-roman 95)))
(assert (= "CMXCIX" (integer-to-roman 999)))
(assert (= "MCDL" (integer-to-roman 1450)))
(assert (= "MDCCXVII" (integer-to-roman 1717)))
(assert (= "MCMXCIX" (integer-to-roman 1999)))
(assert (= "MMMCMXCIX" (integer-to-roman 3999))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment