Here's a little toy to practice my basic Clojure skills. I didn't see  
any similar topic in the archives, so here we go...

This code performs conversions between Roman and Arabic numerals.  
Roman numerals are represented as strings and Arabic numerals are  
just integers. Arabic numerals in the range of 1 and 3999 inclusive  
are allowed. Conventionally, 3999 is the upper limit since the Roman  
numeral for 5000 (a V with a bar over it) cannot be represented in  
ASCII (although it exists in Unicode).

The function roman-to-arabic-aux correctly converts any valid Roman  
numeral. This can be exhaustively tested by comparing the Roman  
numeral output of Common Lisp's FORMAT function for example (e.g.,  
(format nil "~...@r" n)). The trick, however, is weeding out bogus  
strings representing invalid Roman numerals such as IVI, IXV, etc...  
For this purpose the function "roman?" uses a regular expression I  
cribbed from Perl's CPAN module Roman.pm (http://search.cpan.org/ 
~chorny/Roman-1.23/lib/Roman.pm).

arabic-to-roman performs the obvious conversion in the other direction.

Incidentally, the "cond" form in roman-to-arabic-aux seems to me  
harder to read in Clojure than it would be in CL with its additional  
set of grouping parentheses. When you can't fit both the predicate  
and the consequent expression on the same line it gets confusing.

I'd appreciate any feedback regarding my Clojure style. Any more  
natural ways to do things?

Aloha,
David Sletten

(def roman-values-map {\I 1 \V 5 \X 10 \L 50 \C 100 \D 500 \M 1000})

(defn value [roman]
   (get roman-values-map (Character/toUpperCase roman)))

(defn roman? [roman-string]
   (and (not (empty? roman-string))
        (re-matches
         #"(?:M{0,3})(?:D?C{0,3}|C[DM])(?:L?X{0,3}|X[LC])(?:V?I{0,3}|I 
[VX])$"
         roman-string)))

(defn roman-to-arabic-aux [roman-string]
   (cond (empty? roman-string) 0
         (empty? (rest roman-string)) (value (first roman-string))
         (< (value (first roman-string)) (value (second roman-string)))
         (- (roman-to-arabic-aux (rest roman-string))
            (value (first roman-string)))
         :else
         (+ (value (first roman-string))
            (roman-to-arabic-aux (rest roman-string)))))

(defn roman-to-arabic [roman-string]
   (if (roman? roman-string)
     (roman-to-arabic-aux roman-string)
     (format "'%s' is not a valid Roman numeral." roman-string)))

(def arabic-values '((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 arabic-to-roman
   ([n] (if (<= 1 n 3999)
          (apply str (arabic-to-roman n arabic-values))
          (format "%d cannot be converted." n)))
   ([n num-list] (cond (empty? num-list) '()
                       (zero? n) '()
                       :else (let [[[arabic roman] & tail] num-list]
                               (if (>= n arabic)
                                 (cons roman (arabic-to-roman (- n  
arabic)
                                                              num-list))
                                 (arabic-to-roman n tail)))) ))


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To post to this group, send email to clojure@googlegroups.com
To unsubscribe from this group, send email to 
clojure+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to