I’m still in the process of trying to wrap my head around Clojure. I’ve been practicing several different katas like Fizz Buzz and the Bowling Game. Another one I’ve been doing a lot lately is the Roman Numerals kata. This exercise combines both my fascination for Ancient Rome and learning a programming language.

Here is the code of my latest stab at this problem:

(ns roman_numbers.core (:require [clojure.math.numeric-tower :as math]) (:require [clojure.string :as str])) (def symbols (sorted-map-by > 1000 "M" 500 "D" 100 "C" 50 "L" 10 "X" 5 "V", 1 "I")) (defn orderOfMagnitude [number] (math/expt 10 (count (str number)))) (defn determineApplicableSymbols [number] (let [magnitude (orderOfMagnitude number)] (if(<= magnitude 1000) (take 3 (filter (fn[x] (>= magnitude (first x))) symbols)) { -1 "-" -2 "-" 1000 "M" } ) )) (defn selectSymbol [number unitSymbol fiveSymbol tenSymbol] (let [unitSymbolValue (key unitSymbol) unitNumber (quot number unitSymbolValue) total (* unitNumber unitSymbolValue)] (cond (< unitNumber 4) (first { total (apply str (repeat unitNumber (val unitSymbol))) }) (< unitNumber 9) (first { total (str/join (concat (repeat (- 5 unitNumber) (val unitSymbol)) (val fiveSymbol) (repeat (- unitNumber 5) (val unitSymbol))) )}) (= unitNumber 9) (first { total (apply str (val unitSymbol) (val tenSymbol)) }) ) )) (defn convert [number] (loop [currentNumber number result ""] (if (zero? currentNumber) result (let [applicableSymbols (determineApplicableSymbols currentNumber) unitSymbol (last applicableSymbols) fiveSymbol (second applicableSymbols) tenSymbol (first applicableSymbols) matchingSymbol (selectSymbol currentNumber unitSymbol fiveSymbol tenSymbol)] (recur (- currentNumber (key matchingSymbol)) (str result (val matchingSymbol))) ))))

This might seem a bit overwhelming, but don’t let that scare you. Let me explain the gist of this piece of code. When the *convert* function is called with a particular number, let’s say 14, first thing the applicable symbols are determined by calling the function *determineApplicableSymbols*. These are taken from the symbols map depending on the size of the specified number. For this first iteration, the applicable symbols are returned in a sequence containing [100 C] [50 L] [10 X].

The actual meat of the conversion functionality is contained by the *selectSymbol* function. Calling this function returns a matching symbol, which in this case returns [10 X]. The number 10 is subtracted from our initial number and the symbol X is added to the result. Then the recursion starts again, but now *currentNumber* is assigned to the number 4 and result contains the string “X”.

For the second iteration, the applicable symbols are [10 X] [5 V] [1 I] where the *selectSymbol* function converts the number 4 into “IV” which is added to the result.

In the third and final iteration, the *currentNumber* is 0 which breaks out of the loop and returns the result, which is “XIV”.

For completeness sake, here are the unit tests:

(ns roman_numbers.t-core (:use midje.sweet) (:use [roman_numbers.core])) (facts "When converting an Arabic number to a Roman Number" (fact "it returns I for the number 1" (convert 1) => "I") (fact "it returns II for the number 2" (convert 2) => "II") (fact "it returns III for the number 3" (convert 3) => "III") (fact "it returns IV for the number 4" (convert 4) => "IV") (fact "it returns V for the number 5" (convert 5) => "V") (fact "it returns VIII for the number 8" (convert 8) => "VIII") (fact "it returns IX for the number 9" (convert 9) => "IX") (fact "it returns X for the number 10" (convert 10) => "X") (fact "it returns XIV for the number 14" (convert 14) => "XIV") (fact "it returns XXXIX for the number 39" (convert 39) => "XXXIX") (fact "it returns XLIX for the number 49" (convert 49) => "XLIX") (fact "it returns CCLXXVIII for the number 278" (convert 278) => "CCLXXVIII") (fact "it returns CML for the number 950" (convert 950) => "CML") (fact "it returns MDCCCXLV for the number 1845" (convert 1845) => "MDCCCXLV") (fact "it returns MMCCCLXXVIII for the number 2378" (convert 2378) => "MMCCCLXXVIII") )

There are probably better and more idiomatic solutions to solve this problem in Clojure or other programming languages. I would love to see some other solutions as well.

Until next time.

asdasdasd

With the algorithm from

http://www.blackwasp.co.uk/NumberToRoman_2.aspx

(def nums {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”})

(defn roman [n i]

(if (> i 0)

(let [val (first n)

m (if (= 1 (count n))

n

(rest n))]

(if ( (keys nums)) 1992))

;; => “MCMXCII”

Nice one. I’ve been toying with a similar solution. The reason I wanted to try the solution that I posted is that I didn’t want to include IV, IX, XC, … into the list of symbols as they are made up of base symbols (just for the sake of the exercise).