Created
March 20, 2012 19:47
-
-
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
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) | |
(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