You want to convert decimal numbers to Roman numerals and Roman numerals to decimal numbers.
Here is some example code illustrating the conversion between decimals numbers and Roman numerals.
(define (decimal->roman num)
(let ((decimal-numbers (list 1000 900 500 400 100 90 50 40 10 9 5 4 1))
(roman-numbers (list "M" "CM" "D" "CD" "C" "XC" "L"
"XL" "X" "IX" "V" "IV" "I")))
(decimal->roman-aux num "" decimal-numbers roman-numbers)))
(define (decimal->roman-aux num s decimal-numbers roman-numbers)
(if (null? decimal-numbers)
s
(if (>= num (car decimal-numbers))
(decimal->roman-aux (- num (car decimal-numbers))
(string-append s (car roman-numbers))
decimal-numbers roman-numbers)
(decimal->roman-aux num s
(cdr decimal-numbers) (cdr roman-numbers)))))
And here is the reverse transformation (converting between Roman numerals and decimal numbers):
(require (lib "13.ss" "srfi"))
(read-case-sensitive #t)
(define (roman->decimal s)
(cond
[(string= s "") 0]
[(= (string-length s) 1) (case (string->symbol s)
[(I) 1]
[(V) 5]
[(X) 10]
[(L) 50]
[(C) 100]
[(D) 500]
[(M) 1000]
[else 0])]
[else (+ (roman->dec (string-drop s 2))
(case (string->symbol (substring/shared s 0 2))
[(IV) 4]
[(IX) 9]
[(XL) 40]
[(XC) 90]
[(CD) 400]
[(CM) 900]
[else (+ (roman->dec (substring/shared s 0 1))
(roman->dec (string-drop s 1)))]))]))
In the first code sample we show a very simple solution; the main procedure takes the number to be converted. It then calls an auxilliary procedure that performs the actual conversion. The auxilliary procedure takes four arguments: (1) the number we want to convert, (2) an accumulator string that will (eventually) contain the result, (3) a list containing the decimal integers that we can represent as roman numerals, and (4) a list of corresponding roman numeral representations of the decimal values.
We have three cases to consider:
- If the list
decimal-numbers is empty we just return the accumulator string, which is the value we are seeking.
- Otherwise, we need to make a recursive call. There are two possibilities:
- If the number is greater than or equal to the first element of the decimals list:
- Use the difference of the first argument (the number to be converted) and the "value" of the roman numeral we are currently considering for the first argument.
- Append the roman numeral character currently being considered to the accumulator and pass this as the second argument.
- The other two arguments are the decimal and roman lists without changes.
- Otherwise, the number must be less than the first element of the decimal list:
- Use the input number without changes for the first argument.
- Use the existing accumulator string for the second argument.
- The final two arguments are the decimal and roman numeral lists (without their first element).
In the second code example we use
SRFI 13 that defines many procedures for manipulating strings. This problem would be difficult to solve without those helpful implementations.
The main idea of this algorithm is two handle two cases: (1) The first character of the string is a simple symbol (an atom) (I,V,X,L,C,D,M); and (2) the first two characters of the string form a double symbol (IV, IX, XL, XC, CD, CM).
Once we determine which condition we are dealing with, we just sum the decimal values of the symbol that we have recognized.
--
EmiliaBarajas - 18 May 2004
Substantially revised the text of the recipe, but the coding is unchanges.
--
BrentAFulgham - 24 Aug 2004