Monday, August 6, 2012

Roman Numerals in Clojure

I saw this post on Roman Numerals in Clojure:

http://www.jayway.com/2012/08/04/a-decimal-to-roman-numeral-converter-in-just...

This is the sort of little programming problem that can stop useful work dead in its tracks. Here's my shot at the problem. I think it reads better in that the recursion just deals with the numbers and the Roman numeral strings are mapped latter. It's also a bit more efficient in the way it tests against the Roman "bases" (as I call them). There's no need to retest against a high base once your interim value has gone below that mark. In any case, my version is quite a bit faster than the other solution in my micro-benchmarks.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 
(ns miner.roman
  (:require [clojure.test :refer :all]))

;; inspired by
;; http://www.jayway.com/2012/08/04/a-decimal-to-roman-numeral-converter-in-just-a-few-lines/

(def roman-map {1000 "M" 900 "CM" 500 "D" 400 "CD"
                100 "C" 90 "XC" 50 "L" 40 "XL"
                10 "X" 9 "IX" 5 "V" 4 "IV" 1 "I"})

(def roman-bases (sort > (keys roman-map)))

(defn roman-addends [n]
  {:pre [(< 0 n 4000)]}
  (loop [n n bases roman-bases addends []]
    (if (zero? n)
      addends
      (let [base (first bases)]
        (if (>= n base)
          (recur (- n base) bases (conj addends base))
          (recur n (rest bases) addends))))))

(defn roman [n]
  (apply str (map roman-map (roman-addends n))))


;; contrib clojure.pprint/cl-format works but is slow
(defn roman-cl [n]
  (clojure.pprint/cl-format nil "~@R" n))


(deftest roman-4k
  (doseq [n (range 1 4000)]
    (is (roman-cl n) (roman n))))

Posted via email from fnclojure

No comments: