Skip to content

Instantly share code, notes, and snippets.

@titogarcia
Last active March 15, 2020 11:52
Show Gist options
  • Select an option

  • Save titogarcia/13a5837dea9b16b73a58150b08f5b218 to your computer and use it in GitHub Desktop.

Select an option

Save titogarcia/13a5837dea9b16b73a58150b08f5b218 to your computer and use it in GitHub Desktop.
Roman numerals to integer in Clojure
(ns roman
(:require
[clojure.test :refer :all]))
(def roman-letter->int
{\I 1
\V 5
\X 10
\L 50
\C 100
\D 500
\M 1000})
(defn roman->int [roman]
(first
(reduce
(fn [[acc max] elem]
(let [val (roman-letter->int elem)]
(if (>= val max)
[(+ acc val) val]
[(- acc val) max])))
[0 0]
(reverse roman))))
(deftest roman-tests
(is (= 1 (roman->int "I")))
(is (= 2 (roman->int "II")))
(is (= 3 (roman->int "III")))
(is (= 4 (roman->int "IV")))
(is (= 5 (roman->int "V")))
(is (= 40 (roman->int "XL")))
(is (= 1998 (roman->int "MCMXCVIII")))
(is (= 2020 (roman->int "MMXX")))
(is (= 4999 (roman->int "MMMMCMXCIX"))))
(roman-tests)
; Second try: loop state is more explicit and less positional.
(defn roman->int [roman]
(loop [[letter & rest] (reverse roman)
acc 0
max 0]
(if-not letter
acc
(let [val (roman-letter->int letter)]
(if (>= val max)
(recur rest (+ acc val) val) ; 'recur' is still positional
(recur rest (- acc val) max))))))
; Third try: explicit with a map as reduce state
(defn roman->int [roman]
(let [f (fn [{acc :acc
max :max}
elem]
(let [val (roman-letter->int elem)]
(if (>= val max)
{:acc (+ acc val)
:max val}
{:acc (- acc val)
:max max})))]
(:acc
(reduce f {:acc 0 :max 0} (reverse roman)))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment