This is an update of an older post describing a short program written in ALGOL 68 to convert strings of roman numbers to decimal integer values.
For no particular reason I decided to rewrite this using two operators to do the roman number conversion for a single roman numeral (the simplest case) and strings of roman numbers, and I added a simple procedure to print and check the results. I think the resulting code is a little more ‘elegant’ but that is just my personal view.
PROGRAM roman
{ This program is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License along
with this program. If not, see <http://www.gnu.org/licenses/>
(
OP DECIMAL = (CHAR roman) INT:
{ Degenerate case corresponding to a single roman numeral, used
when parsing longer strings of roman numerals. An unrecognized
numeral or null string will return the value zero.
Note - To make the code more readable the tokenized syntax of the
if-then statement has been used. }
( roman = "M" | 1000 |:
roman = "D" | 500 |:
roman = "C" | 100 |:
roman = "L" | 50 |:
roman = "X" | 10 |:
roman = "V" | 5 |:
roman = "I" | 1 | 0);
OP DECIMAL = (STRING roman) INT:
{ Overloaded definition of the operator for more complex strings of roman
numerals. }
( INT result := 0, last := 0, run := 0;
FOR i FROM LWB roman TO UPB roman DO
INT value = DECIMAL(roman[i]);
IF last = value THEN
run +:= value
ELSE
IF last < value THEN
result -:= run
ELSE
result +:= run
FI;
run := last := value
FI
OD;
result +:= run );
PRIO DECIMAL = 9;
PROC check = (STRING roman, INT value) VOID:
( write ((roman," "*(12-UPB(roman)),"= ", fixed(DECIMAL(roman),-6,0),
((DECIMAL(roman) = value | " - Ok" | " - Error")),
newline)) );
check ("MMXI",2011);
check ("MIM", 1999);
check ("MCMXCIX", 1999);
check ("MCMLXIII", 1963);
check ("MDCLXIV", 1664);
check ("XXCIII", 83);
check ("LXXIIX", 78);
check ("MDCCCCX", 1910);
write (newline);
check ("IX", 9);
check ("VII", 7);
check ("V", 5);
check ("IV", 4);
check ("III", 3);
check ("II", 2);
check ("I", 1);
check ("", 0) )
FINISH.
The program produces the following output. Both possible forms for 1999 are handled correctly but while ‘MIM’ is shorter and more convenient, the longer version ‘MCMXCIX’ is normally used.
MIM = 1999 - Ok
MCMXCIX = 1999 - Ok
MCMLXIII = 1963 - Ok
MDCLXIV = 1664 - Ok
XXCIII = 83 - Ok
LXXIIX = 78 - Ok
MDCCCCX = 1910 - Ok
IX = 9 - Ok
VII = 7 - Ok
V = 5 - Ok
IV = 4 - Ok
III = 3 - Ok
II = 2 - Ok
I = 1 - Ok
= 0 - Ok
Algol68g Notes
To run this example on a modern system using Algol68g then you will need to make a couple of minor changes to my code.
- Comments should be delimited using ‘#’ rather than ‘{‘ and ‘}’
- The ‘PROGRAM’ and ‘FINISH’ statements should be omitted, but you do need to start your program with an opening bracket (or ‘BEGIN’).