Roman numerals/Decode: Difference between revisions

From Rosetta Code
Content added Content deleted
(Add a declarative way of achieving the transformation)
 
(382 intermediate revisions by more than 100 users not shown)
Line 1: Line 1:
{{task}}
{{task}}
Create a function that takes a Roman numeral as its argument and returns its value as a numeric decimal integer. You don't need to validate the form of the Roman numeral.


;Task:
Modern Roman numerals are written by expressing each decimal digit of the number to be encoded separately, starting with the leftmost digit and skipping any 0s. So 1990 is rendered "MCMXC" (1000 = M, 900 = CM, 90 = XC) and 2008 is rendered "MMVIII" (2000 = MM, 8 = VIII). The Roman numeral for 1666, "MDCLXVI", uses each letter in descending order.
Create a function that takes a Roman numeral as its argument and returns its value as a numeric decimal integer.

You don't need to validate the form of the Roman numeral.

Modern Roman numerals are written by expressing each decimal digit of the number to be encoded separately,
<br>starting with the leftmost decimal digit and skipping any '''0'''s &nbsp; (zeroes).

'''1990''' is rendered as &nbsp; '''MCMXC''' &nbsp; &nbsp; (1000 = M, &nbsp; 900 = CM, &nbsp; 90 = XC) &nbsp; &nbsp; and
<br>'''2008''' is rendered as &nbsp; '''MMVIII''' &nbsp; &nbsp; &nbsp; (2000 = MM, &nbsp; 8 = VIII).
The Roman numeral for '''1666''', &nbsp; '''MDCLXVI''', &nbsp; uses each letter in descending order.
<br><br>

=={{header|11l}}==
{{trans|Python}}

<syntaxhighlight lang="11l">V roman_values = [(‘I’, 1), (‘IV’, 4), (‘V’, 5), (‘IX’, 9), (‘X’, 10),
(‘XL’, 40), (‘L’, 50), (‘XC’, 90), (‘C’, 100),
(‘CD’, 400), (‘D’, 500), (‘CM’, 900), (‘M’, 1000)]

F roman_value(=roman)
V total = 0
L(symbol, value) reversed(:roman_values)
L roman.starts_with(symbol)
total += value
roman = roman[symbol.len..]
R total

L(value) [‘MCMXC’, ‘MMVIII’, ‘MDCLXVI’]
print(value‘ = ’roman_value(value))</syntaxhighlight>

{{out}}
<pre>
MCMXC = 1990
MMVIII = 2008
MDCLXVI = 1666
</pre>

=={{header|360 Assembly}}==
<syntaxhighlight lang="360asm">* Roman numerals Decode - 17/04/2019
ROMADEC CSECT
USING ROMADEC,R13 base register
B 72(R15) skip savearea
DC 17F'0' savearea
SAVE (14,12) save previous context
ST R13,4(R15) link backward
ST R15,8(R13) link forward
LR R13,R15 set addressability
LA R6,1 i=1
DO WHILE=(C,R6,LE,=A(NV)) do i=1 to hbound(vals)
LR R1,R6 i
SLA R1,3 ~
LA R4,VALS-L'VALS(R1) @vals(i)
MVC X,0(R4) x=vals(i)
SR R9,R9 prev=0
ST R9,Y y=0
LA R7,L'X j=1
DO WHILE=(C,R7,GE,=A(1)) do j=length(x) to 1 by -1
LA R4,X-1 @x
AR R4,R7 +j
MVC C,0(R4) c=substr(x,j,1)
IF CLI,C,NE,C' ' THEN if c^=' ' then
SR R1,R1 r1=0
LA R2,1 k=1
DO WHILE=(C,R2,LE,=A(L'ROMAN)) do k=1 to length(roman)
LA R3,ROMAN-1 @roman
AR R3,R2 +k
IF CLC,0(L'C,R3),EQ,C THEN if substr(roman,k,1)=c
LR R1,R2 index=k
B REINDEX leave k
ENDIF , endif
LA R2,1(R2) k=k+1
ENDDO , enddo k
REINDEX EQU * r1=index(roman,c)
SLA R1,2 ~
L R8,DECIM-4(R1) n=decim(index(roman,c))
IF CR,R8,LT,R9 THEN if n<prev then
LCR R8,R8 n=-n
ENDIF , endif
L R2,Y y
AR R2,R8 +n
ST R2,Y y=y+n
LR R9,R8 prev=n
ENDIF , endif
BCTR R7,0 j--
ENDDO , enddo j
MVC PG(8),X x
L R1,Y y
XDECO R1,XDEC edit y
MVC PG+12(4),XDEC+8 output y
XPRNT PG,L'PG print buffer
LA R6,1(R6) i++
ENDDO , enddo i
L R13,4(0,R13) restore previous savearea pointer
RETURN (14,12),RC=0 restore registers from calling sav
NV EQU (X-VALS)/L'VALS
ROMAN DC CL7'MDCLXVI'
DECIM DC F'1000',F'500',F'100',F'50',F'10',F'5',F'1'
VALS DC CL8'XIV',CL8'CMI',CL8'MIC',CL8'MCMXC',CL8'MDCLXVI'
DC CL8'MMVIII',CL8'MMXIX',CL8'MMMCMXCV'
X DS CL(L'VALS)
Y DS F
C DS CL1
PG DC CL80'........ -> ....'
XDEC DS CL12
REGEQU
END ROMADEC </syntaxhighlight>
{{out}}
<pre>
XIV -> 14
CMI -> 901
MIC -> 1099
MCMXC -> 1990
MDCLXVI -> 1666
MMVIII -> 2008
MMXIX -> 2019
MMMCMXCV -> 3995
</pre>

=={{header|8080 Assembly}}==

The routine at <code>roman</code> takes the address of a zero-terminated string in BC,
and returns the value of the Roman number in that string as a 16-bit integer in HL.
The Roman numeral must be in uppercase letters.

<syntaxhighlight lang="8080asm"> org 100h
jmp test
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Takes a zero-terminated Roman numeral string in BC
;; and returns 16-bit integer in HL.
;; All registers destroyed.
roman: dcx b
romanfindend: inx b ; load next character
ldax b
inr e
ana a ; are we there yet
jnz romanfindend
lxi h,0 ; zero HL to hold the total
push h ; stack holds the previous roman numeral
romanloop: dcx b ; get next roman numeral
ldax b ; (work backwards)
call romandgt
jc romandone ; carry set = not Roman anymore
xthl ; load previous roman numeral
call cmpdehl ; DE < HL?
mov h,d ; in any case, DE is now the previous
mov l,e ; Roman numeral
xthl ; bring back the total
jnc romanadd
mov a,d ; DE (current) < HL (previous)
cma ; so this Roman digit must be subtracted
mov d,a ; from the total.
mov a,e ; so we negate it before adding it
cma ; two's complement: -de = (~de)+1
mov e,a
inx d
romanadd: dad d ; add to running total
jmp romanloop
romandone: pop d ; remove temporary variable from stack
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 16-bit compare DE with HL, set flags
;; accordingly. A destroyed.
cmpdehl: mov a,d
cmp h
rnz
mov a,e
cmp l
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Takes a single Roman 'digit' in A,
;; and returns its value in DE (0 if invalid)
;; All other registers preserved.
romandgt: push h ; preserve hl for the caller
lxi h,romantab
mvi e,7 ; e=counter
romandgtl: cmp m ; check table entry
jz romanfound
inx h ; move to next table entry
inx h
inx h
dcr e ; decrease counter
jnz romandgtl
pop h ; we didn't find it
stc ; set carry
ret ; return with DE=0
romanfound: inx h ; we did find it
mov e,m ; load it into DE
inx h
mov d,m
pop h
ana a ; clear carry
ret
romantab: db 'I',1,0 ; 16-bit little endian values
db 'V',5,0
db 'X',10,0
db 'L',50,0
db 'C',100,0
db 'D',0f4h,1
db 'M',0e8h,3
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; The following is testing and I/O code.
test: mvi c,10 ; read string from console
lxi d,bufdef
call 5
mvi c,9 ; print newline
lxi d,nl
call 5
lxi b,buf ; run `roman' on the input string
call roman ; the result is now in hl
lxi d,-10000
call numout ; print 10000s digit
lxi d,-1000
call numout ; print 1000s digit
lxi d,-100
call numout ; print 100s digit
lxi d,-10
call numout ; print 10s digit
lxi d,-1 ; ...print 1s digit
numout: mvi a,-1
push h
numloop: inr a
pop b
push h
dad d
jc numloop
adi '0'
mvi c,2
mov e,a
call 5
pop h
ret
nl: db 13,10,'$'
bufdef: db 16,0
buf: ds 17</syntaxhighlight>

=={{header|Action!}}==
<syntaxhighlight lang="action!">CARD FUNC DecodeRomanDigit(CHAR c)
IF c='I THEN RETURN (1)
ELSEIF c='V THEN RETURN (5)
ELSEIF c='X THEN RETURN (10)
ELSEIF c='L THEN RETURN (50)
ELSEIF c='C THEN RETURN (100)
ELSEIF c='D THEN RETURN (500)
ELSEIF c='M THEN RETURN (1000)
FI
RETURN (0)

CARD FUNC DecodeRomanNumber(CHAR ARRAY s)
CARD res,curr,prev
BYTE i

res=0 prev=0 i=s(0)
WHILE i>0
DO
curr=DecodeRomanDigit(s(i))
IF curr<prev THEN
res==-curr
ELSE
res==+curr
FI
prev=curr
i==-1
OD
RETURN (res)

PROC Test(CHAR ARRAY s)
CARD n
n=DecodeRomanNumber(s)
PrintF("%S=%U%E",s,n)
RETURN

PROC Main()
Test("MCMXC")
Test("MMVIII")
Test("MDCLXVI")
Test("MMMDCCCLXXXVIII")
Test("MMMCMXCIX")
RETURN</syntaxhighlight>
{{out}}
[https://gitlab.com/amarok8bit/action-rosetta-code/-/raw/master/images/Roman_numerals_decode.png Screenshot from Atari 8-bit computer]
<pre>
MCMXC=1990
MMVIII=2008
MDCLXVI=1666
MMMDCCCLXXXVIII=3888
MMMCMXCIX=3999
</pre>


=={{header|Ada}}==
=={{header|Ada}}==


<syntaxhighlight lang="ada">Pragma Ada_2012;
Translation from Delphi
Pragma Assertion_Policy( Check );


With
<lang Ada>with Ada.Text_IO;
Unchecked_Conversion,
Ada.Text_IO;


Procedure Test_Roman_Numerals is
procedure Decode_Roman_Numerals is


-- We create an enumeration of valid characters, note that they are
function Roman_To_Integer(A_Roman: String) return Integer is
-- character-literals, this is so that we can use literal-strings,
-- and that their size is that of Integer.
Type Roman_Digits is ('I', 'V', 'X', 'L', 'C', 'D', 'M' )
with Size => Integer'Size;


-- We use a representation-clause ensure the proper integral-value
function Decode_Roman_Digit (A_Char: Character) return Integer is
-- of each individual character.
begin
For Roman_Digits use
case A_Char is
(
when 'M' | 'm' => return 1000;
when 'D' | 'd' => return 500;
'I' => 1,
when 'C' | 'c' => return 100;
'V' => 5,
when 'L' | 'l' => return 50;
'X' => 10,
when 'X' | 'x' => return 10;
'L' => 50,
when 'V' | 'v' => return 5;
'C' => 100,
when 'I' | 'i' => return 1;
'D' => 500,
when others => return 0;
'M' => 1000
end case;
);
end Decode_Roman_Digit;


-- To convert a Roman_Digit to an integer, we now only need to
L_Curr_Val: Integer;
-- read its value as an integer.
L_Last_Val: Integer;
Function Convert is new Unchecked_Conversion
( Source => Roman_Digits, Target => Integer );


-- Romena_Numeral is a string of Roman_Digit.
Result: Integer;
Type Roman_Numeral is array (Positive range <>) of Roman_Digits;
begin
Result := 0;


-- The Numeral_List type is used herein only for testing
L_Last_Val := 0;
-- and verification-data.
Type Numeral_List is array (Positive range <>) of
not null access Roman_Numeral;


-- The Test_Cases subtype ensures that Test_Data and Validation_Data
for i in reverse 1 .. A_Roman'Length loop
-- both contain the same number of elements, and that the indecies
L_Curr_Val := Decode_Roman_Digit(A_Roman(I));
-- are the same; essentially the same as:
if L_Curr_Val < L_Last_Val then
--
Result := Result - L_Curr_Val;
-- pragma Assert( Test_Data'Length = Validation_Data'Length
else
-- AND Test_Data'First = Validation_Data'First);
Result := Result + L_Curr_Val;
end if;
L_Last_Val := L_Curr_Val;
end loop;
return Result;
end Roman_To_Integer;


subtype Test_Cases is Positive range 1..14;
begin

Ada.Text_IO.Put_Line(Integer'Image(Roman_To_Integer("MCMXC"))); -- 1990
Test_Data : constant Numeral_List(Test_Cases):=
Ada.Text_IO.Put_Line(Integer'Image(Roman_To_Integer("MMVIII"))); -- 2008
(
Ada.Text_IO.Put_Line(Integer'Image(Roman_To_Integer("MDCLXVI"))); -- 1666
New Roman_Numeral'("III"), -- 3
end Decode_Roman_Numerals;</lang>
New Roman_Numeral'("XXX"), -- 30
New Roman_Numeral'("CCC"), -- 300
New Roman_Numeral'("MMM"), -- 3000

New Roman_Numeral'("VII"), -- 7
New Roman_Numeral'("LXVI"), -- 66
New Roman_Numeral'("CL"), -- 150
New Roman_Numeral'("MCC"), -- 1200

New Roman_Numeral'("IV"), -- 4
New Roman_Numeral'("IX"), -- 9
New Roman_Numeral'("XC"), -- 90

New Roman_Numeral'("ICM"), -- 901
New Roman_Numeral'("CIM"), -- 899
New Roman_Numeral'("MDCLXVI") -- 1666
);

Validation_Data : constant array(Test_Cases) of Natural:=
( 3, 30, 300, 3000,
7, 66, 150, 1200,
4, 9, 90,
901, 899,
1666
);


-- In Roman numerals, the subtractive form [IV = 4] was used
-- very infrequently, the most common form was the addidive
-- form [IV = 6]. (Consider military logistics and squads.)

-- SUM returns the Number, read in the additive form.
Function Sum( Number : Roman_Numeral ) return Natural is
begin
Return Result : Natural:= 0 do
For Item of Number loop
Result:= Result + Convert( Item );
end loop;
End Return;
end Sum;

-- EVAL returns Number read in the subtractive form.
Function Eval( Number : Roman_Numeral ) return Natural is
Current : Roman_Digits:= 'I';
begin
Return Result : Natural:= 0 do
For Item of Number loop
if Current < Item then
Result:= Convert(Item) - Result;
Current:= Item;
else
Result:= Result + Convert(Item);
end if;
end loop;
End Return;
end Eval;

-- Display the given Roman_Numeral via Text_IO.
Procedure Put( S: Roman_Numeral ) is
begin
For Ch of S loop
declare
-- The 'Image attribute returns the character inside
-- single-quotes; so we select the character itself.
C : Character renames Roman_Digits'Image(Ch)(2);
begin
Ada.Text_IO.Put( C );
end;
end loop;
end;

-- This displays pass/fail dependant on the parameter.
Function PF ( Value : Boolean ) Return String is
begin
Return Result : String(1..4):= ( if Value then"pass"else"fail" );
End PF;

Begin
Ada.Text_IO.Put_Line("Starting Test:");

for Index in Test_Data'Range loop
declare
Item : Roman_Numeral renames Test_Data(Index).all;
Value : constant Natural := Eval(Item);
begin
Put( Item );

Ada.Text_IO.Put( ASCII.HT & "= ");
Ada.Text_IO.Put( Value'Img );
Ada.Text_IO.Put_Line( ASCII.HT & '[' &
PF( Value = Validation_Data(Index) )& ']');
end;
end loop;

Ada.Text_IO.Put_Line("Testing complete.");
End Test_Roman_Numerals;
</syntaxhighlight>

{{out}}
<pre>Starting Test:
III = 3 [pass]
XXX = 30 [pass]
CCC = 300 [pass]
MMM = 3000 [pass]
VII = 7 [pass]
LXVI = 66 [pass]
CL = 150 [pass]
MCC = 1200 [pass]
IV = 4 [pass]
IX = 9 [pass]
XC = 90 [pass]
ICM = 901 [pass]
CIM = 899 [pass]
MDCLXVI = 1666 [pass]
Testing complete.</pre>

=={{header|ALGOL 68}}==
{{works with|ALGOL 68G|Any - tested with release 2.2.0}}
Note: roman to int will handle multiple subtraction, e.g. IIIIX for 6.
<syntaxhighlight lang="algol68"> PROC roman to int = (STRING roman) INT:
BEGIN
PROC roman digit value = (CHAR roman digit) INT:
(roman digit = "M" | 1000 |:
roman digit = "D" | 500 |:
roman digit = "C" | 100 |:
roman digit = "L" | 50 |:
roman digit = "X" | 10 |:
roman digit = "V" | 5 |:
roman digit = "I" | 1);

INT result := 0, previous value := 0, run := 0;
FOR i FROM LWB roman TO UPB roman
DO
INT value = roman digit value(roman[i]);
IF previous value = value THEN
run +:= value
ELSE
IF previous value < value THEN
result -:= run
ELSE
result +:= run
FI;
run := previous value := value
FI
OD;
result +:= run
END;

MODE TEST = STRUCT (STRING input, INT expected output);
[] TEST roman test = (
("MMXI", 2011), ("MIM", 1999),
("MCMLVI", 1956), ("MDCLXVI", 1666),
("XXCIII", 83), ("LXXIIX", 78),
("IIIIX", 6)
);
print(("Test input Value Got", newline, "--------------------------", newline));
FOR i FROM LWB roman test TO UPB roman test
DO
INT output = roman to int(input OF roman test[i]);
printf(($g, n (12 - UPB input OF roman test[i]) x$, input OF roman test[i]));
printf(($g(5), 1x, g(5), 1x$, expected output OF roman test[i], output));
printf(($b("ok", "not ok"), 1l$, output = expected output OF roman test[i]))
OD</syntaxhighlight>

=={{header|ALGOL W}}==
<syntaxhighlight lang="algolw">begin
% decodes a roman numeral into an integer %
% there must be at least one blank after the numeral %
% This takes a lenient view on roman numbers so e.g. IIXX is 18 - see %
% the Discussion %
integer procedure romanToDecimal ( string(32) value roman ) ;
begin
integer decimal, rPos, currDigit, nextDigit, seqValue;
string(1) rDigit;

% the roman number is a sequence of sequences of roman digits %
% if the previous sequence is of higher value digits than the next, %
% the higher value is added to the overall value %
% if the previous seequence is of lower value, it is subtracted %
% e.g. MCMLXII %
% the sequences are M, C, M, X, II %
% M is added, C subtracted, M added, X added and II added %

% get the value of a sequence of roman digits %
integer procedure getSequence ;
if rDigit = " " then begin
% end of the number %
0
end
else begin
% have another sequence %
integer sValue;
sValue := 0;
while roman( rPos // 1 ) = rDigit do begin
sValue := sValue + currDigit;
rPos := rPos + 1;
end while_have_same_digit ;
% remember the next digit %
rDigit := roman( rPos // 1 );
% result is the sequence value %
sValue
end getSequence ;

% convert a roman digit into its decimal equivalent %
% an invalid digit will terminate the program, " " is 0 %
integer procedure getValue( string(1) value romanDigit ) ;
if romanDigit = "m" or romanDigit = "M" then 1000
else if romanDigit = "d" or romanDigit = "D" then 500
else if romanDigit = "c" or romanDigit = "C" then 100
else if romanDigit = "l" or romanDigit = "L" then 50
else if romanDigit = "x" or romanDigit = "X" then 10
else if romanDigit = "v" or romanDigit = "V" then 5
else if romanDigit = "i" or romanDigit = "I" then 1
else if romanDigit = " " then 0
else begin
write( s_w := 0, "Invalid roman digit: """, romanDigit, """" );
assert false;
0
end getValue ;

% get the first sequence %
decimal := 0;
rPos := 0;
rDigit := roman( rPos // 1 );
currDigit := getValue( rDigit );
seqValue := getSequence;

% handle the sequences %
while rDigit not = " " do begin
% have another sequence %
nextDigit := getValue( rDigit );
if currDigit < nextDigit
then % prev digit is lower % decimal := decimal - seqValue
else % prev digit is higher % decimal := decimal + seqValue
;
currDigit := nextDigit;
seqValue := getSequence;
end while_have_a_roman_digit ;

% add the final sequence %
decimal + seqValue
end roman ;

% test the romanToDecimal routine %

procedure testRoman ( string(32) value romanNumber ) ;
write( i_w := 5, romanNumber, romanToDecimal( romanNumber ) );

testRoman( "I" ); testRoman( "II" );
testRoman( "III" ); testRoman( "IV" );
testRoman( "V" ); testRoman( "VI" );
testRoman( "VII" ); testRoman( "VIII" );
testRoman( "IX" ); testRoman( "IIXX" );
testRoman( "XIX" ); testRoman( "XX" );
write( "..." );
testRoman( "MCMXC" );
testRoman( "MMVIII" );
testRoman( "MDCLXVI" );

end.</syntaxhighlight>
{{out}}
<pre>
I 1
II 2
III 3
IV 4
V 5
VI 6
VII 7
VIII 8
IX 9
IIXX 18
XIX 19
XX 20
...
MCMXC 1990
MMVIII 2008
MDCLXVI 1666
</pre>

=={{header|ANTLR}}==
[[File:Rn.PNG|left|Roman Numeral]]
[[File:Hundreds.PNG|left|Roman Numeral]]
[[File:H5.PNG|left|Roman Numeral]]
[[File:H9.PNG|left|Roman Numeral]]
[[File:Tens.PNG|left|Roman Numeral]]
[[File:T5.PNG|left|Roman Numeral]]
[[File:T9.PNG|left|Roman Numeral]]
[[File:Units.PNG|left|Roman Numeral]]
[[File:U5.PNG|left|Roman Numeral]]
[[File:U9.PNG|left|Roman Numeral]]
<br clear=both>

===Java===
<syntaxhighlight lang="java">/* Parse Roman Numerals
Nigel Galloway March 16th., 2012
*/
grammar ParseRN ;

options {
language = Java;
}
@members {
int rnValue;
int ONE;
}

parseRN: ({rnValue = 0;} rn NEWLINE {System.out.println($rn.text + " = " + rnValue);})*
;
rn : (Thousand {rnValue += 1000;})* hundreds? tens? units?;

hundreds: {ONE = 0;} (h9 | h5) {if (ONE > 3) System.out.println ("Too many hundreds");};
h9 : Hundred {ONE += 1;} (FiveHund {rnValue += 400;}| Thousand {rnValue += 900;}|{rnValue += 100;} (Hundred {rnValue += 100; ONE += 1;})*);
h5 : FiveHund {rnValue += 500;} (Hundred {rnValue += 100; ONE += 1;})*;

tens : {ONE = 0;} (t9 | t5) {if (ONE > 3) System.out.println ("Too many tens");};
t9 : Ten {ONE += 1;} (Fifty {rnValue += 40;}| Hundred {rnValue += 90;}|{rnValue += 10;} (Ten {rnValue += 10; ONE += 1;})*);
t5 : Fifty {rnValue += 50;} (Ten {rnValue += 10; ONE += 1;})*;
units : {ONE = 0;} (u9 | u5) {if (ONE > 3) System.out.println ("Too many ones");};
u9 : One {ONE += 1;} (Five {rnValue += 4;}| Ten {rnValue += 9;}|{rnValue += 1;} (One {rnValue += 1; ONE += 1;})*);
u5 : Five {rnValue += 5;} (One {rnValue += 1; ONE += 1;})*;
One : 'I';
Five : 'V';
Ten : 'X';
Fifty : 'L';
Hundred: 'C';
FiveHund: 'D';
Thousand: 'M' ;
NEWLINE: '\r'? '\n' ;</syntaxhighlight>
Using this test data:
<pre>
MMXI
MCMLVI
XXCIII
MCMXC
MMVIII
MDCLXVI
IIIIX
MIM
MDCLXVI
LXXIIX
M
MCXI
CMXI
MCM
MMIX
MCDXLIV
MMXII
</pre>
Produces:
<pre>
MMXI = 2011
MCMLVI = 1956
line 3:2 missing NEWLINE at 'C'
XX = 20
CIII = 103
</pre>
Note that this implementation does not accept XXC as eighty. The error is detected and ANTLR attempts to continue by inserting the expected NEWLINE after XX and treating CIII as a new Number.<!--[[User:Nigel Galloway|Nigel Galloway]] 14:55, 17 March 2012 (UTC)--> <!-- contributions not normally signed visually; info is in history -->
<pre>
MCMXC = 1990
MMVIII = 2008
MDCLXVI = 1666
Too many ones
line 7:4 extraneous input 'X' expecting NEWLINE
IIII = 4
</pre>
An implementation above thinks IIIIX is 6. It isn't. ANTLR detects the surfiet of 'I' reports the errors and tries to carry on.<!--[[User:Nigel Galloway|Nigel Galloway]] 14:55, 17 March 2012 (UTC)--> <!-- contributions not normally signed visually; info is in history -->
<pre>
line 8:2 no viable alternative at input 'M'
MIM = 1000
MDCLXVI = 1666
line 10:5 extraneous input 'X' expecting NEWLINE
LXXII = 72
M = 1000
MCXI = 1111
CMXI = 911
MCM = 1900
MMIX = 2009
MCDXLIV = 1444
MMXII = 2012
</pre>

=={{header|APL}}==
{{works with|Dyalog APL}}
<syntaxhighlight lang="apl">fromRoman←{
rmn←(⎕A,⎕A,'*')[(⎕A,⎕UCS 96+⍳26)⍳⍵] ⍝ make input uppercase
dgt←↑'IVXLCDM' (1 5 10 50 100 500 1000) ⍝ values of roman digits
~rmn∧.∊⊂dgt[1;]:⎕SIGNAL 11 ⍝ domain error if non-roman input
map←dgt[2;dgt[1;]⍳rmn] ⍝ map digits to values
+/map×1-2×(2</map),0 ⍝ subtractive principle
}</syntaxhighlight>

{{out}}

<pre> fromRoman¨ 'MCMXC' 'MMVIII' 'MDCLXVI' 'MMXXI'
1990 2008 1666 2021</pre>

=={{header|AppleScript}}==
====isPrefixOf====
{{trans|JavaScript}}
(Functional ES5 version)
{{trans|Haskell}}
<syntaxhighlight lang="applescript">
------------- INTEGER VALUE OF A ROMAN STRING ------------

-- romanValue :: String -> Int
on romanValue(s)
script roman
property mapping : [["M", 1000], ["CM", 900], ["D", 500], ["CD", 400], ¬
["C", 100], ["XC", 90], ["L", 50], ["XL", 40], ["X", 10], ["IX", 9], ¬
["V", 5], ["IV", 4], ["I", 1]]
-- Value of first Roman glyph + value of remaining glyphs
-- toArabic :: [Char] -> Int
on toArabic(xs)
-- transcribe :: (String, Number) -> Maybe (Number, [String])
script transcribe
on |λ|(pair)
set {r, v} to pair
if isPrefixOf(characters of r, xs) then
-- Value of this matching glyph, with any remaining glyphs
{v, drop(length of r, xs)}
else
{}
end if
end |λ|
end script
if 0 < length of xs then
set parsed to concatMap(transcribe, mapping)
(item 1 of parsed) + toArabic(item 2 of parsed)
else
0
end if
end toArabic
end script
toArabic(characters of s) of roman
end romanValue

--------------------------- TEST -------------------------
on run
map(romanValue, {"MCMXC", "MDCLXVI", "MMVIII"})
--> {1990, 1666, 2008}
end run


-------------------- GENERIC FUNCTIONS -------------------

-- concatMap :: (a -> [b]) -> [a] -> [b]
on concatMap(f, xs)
set lng to length of xs
set acc to {}
tell mReturn(f)
repeat with i from 1 to lng
set acc to acc & (|λ|(item i of xs, i, xs))
end repeat
end tell
if {text, string} contains class of xs then
acc as text
else
acc
end if
end concatMap


-- drop :: Int -> [a] -> [a]
-- drop :: Int -> String -> String
on drop(n, xs)
set c to class of xs
if script is not c then
if string is not c then
if n < length of xs then
items (1 + n) thru -1 of xs
else
{}
end if
else
if n < length of xs then
text (1 + n) thru -1 of xs
else
""
end if
end if
else
take(n, xs) -- consumed
return xs
end if
end drop


-- isPrefixOf :: [a] -> [a] -> Bool
-- isPrefixOf :: String -> String -> Bool
on isPrefixOf(xs, ys)
-- isPrefixOf takes two lists or strings and returns
-- true if and only if the first is a prefix of the second.
script go
on |λ|(xs, ys)
set intX to length of xs
if intX < 1 then
true
else if intX > length of ys then
false
else if class of xs is string then
(offset of xs in ys) = 1
else
set {xxt, yyt} to {uncons(xs), uncons(ys)}
((item 1 of xxt) = (item 1 of yyt)) and ¬
|λ|(item 2 of xxt, item 2 of yyt)
end if
end |λ|
end script
go's |λ|(xs, ys)
end isPrefixOf


-- length :: [a] -> Int
on |length|(xs)
set c to class of xs
if list is c or string is c then
length of xs
else
(2 ^ 29 - 1) -- (maxInt - simple proxy for non-finite)
end if
end |length|


-- map :: (a -> b) -> [a] -> [b]
on map(f, xs)
tell mReturn(f)
set lng to length of xs
set lst to {}
repeat with i from 1 to lng
set end of lst to |λ|(item i of xs, i, xs)
end repeat
return lst
end tell
end map


-- Lift 2nd class handler function into 1st class script wrapper
-- mReturn :: Handler -> Script
on mReturn(f)
if class of f is script then
f
else
script
property |λ| : f
end script
end if
end mReturn


-- uncons :: [a] -> Maybe (a, [a])
on uncons(xs)
set lng to |length|(xs)
if 0 = lng then
missing value
else
if (2 ^ 29 - 1) as integer > lng then
if class of xs is string then
set cs to text items of xs
{item 1 of cs, rest of cs}
else
{item 1 of xs, rest of xs}
end if
else
set nxt to take(1, xs)
if {} is nxt then
missing value
else
{item 1 of nxt, xs}
end if
end if
end if
end uncons</syntaxhighlight>
{{Out}}
<syntaxhighlight lang="applescript">{1990, 1666, 2008}</syntaxhighlight>

====Fold right – subtracting or adding====
{{Works with|Yosemite onwards}}
{{trans|Haskell}}
<syntaxhighlight lang="applescript">use framework "Foundation"

----------- INTEGER VALUE OF ROMAN NUMBER STRING ---------

-- fromRoman :: String -> Int
on fromRoman(s)
script subtractIfLower
on |λ|(l, rn)
set {r, n} to rn
if l ≥ r then -- Digit values that increase (L to R),
{l, n + l} -- (added)
else
{l, n - l} -- Digit values that go down: subtracted.
end if
end |λ|
end script
item 2 of foldr(subtractIfLower, {0, 0}, ¬
map(my charVal, characters of s))
end fromRoman


-- charVal :: Char -> Int
on charVal(C)
set v to lookup(toUpper(C), ¬
{I:1, |V|:5, X:10, |L|:50, C:100, D:500, M:1000})
if missing value is v then
0
else
v
end if
end charVal


--------------------------- TEST -------------------------
on run
map(fromRoman, ¬
{"MDCLXVI", "MCMXC", "MMVIII", "MMXVI", "MMXXI"})
--> {1666, 1990, 2008, 2016, 2021}
end run


-------------------- GENERIC FUNCTIONS -------------------

-- foldr :: (a -> b -> b) -> b -> [a] -> b
on foldr(f, startValue, xs)
tell mReturn(f)
set v to startValue
set lng to length of xs
repeat with I from lng to 1 by -1
set v to |λ|(item I of xs, v)
end repeat
return v
end tell
end foldr


-- lookup :: a -> Dict -> Maybe b
on lookup(k, dct)
-- Just the value of k in the dictionary,
-- or missing value if k is not found.
set ca to current application
set v to (ca's NSDictionary's dictionaryWithDictionary:dct)'s ¬
objectForKey:k
if missing value ≠ v then
item 1 of ((ca's NSArray's arrayWithObject:v) as list)
else
missing value
end if
end lookup


-- map :: (a -> b) -> [a] -> [b]
on map(f, xs)
-- The list obtained by applying f
-- to each element of xs.
tell mReturn(f)
set lng to length of xs
set lst to {}
repeat with I from 1 to lng
set end of lst to |λ|(item I of xs, I, xs)
end repeat
return lst
end tell
end map


-- Lift 2nd class handler function into 1st class
-- script wrapper
-- mReturn :: Handler -> Script
on mReturn(f)
if class of f is script then
f
else
script
property |λ| : f
end script
end if
end mReturn


-- toUpper :: String -> String
on toUpper(str)
tell current application
((its (NSString's stringWithString:(str)))'s ¬
uppercaseStringWithLocale:¬
(its NSLocale's currentLocale())) as text
end tell
end toUpper</syntaxhighlight>
{{Out}}
<syntaxhighlight lang="applescript">{1666, 1990, 2008, 2016, 2021}</syntaxhighlight>

=={{header|BASIC}}==
==={{header|Applesoft BASIC}}===
{{trans|BBC BASIC}}
<syntaxhighlight lang="gwbasic"> 10 LET R$ = "MCMXCIX"
20 GOSUB 100 PRINT "ROMAN NUMERALS DECODED"
30 LET R$ = "MMXII"
40 GOSUB 100
50 LET R$ = "MDCLXVI"
60 GOSUB 100
70 LET R$ = "MMMDCCCLXXXVIII"
80 GOSUB 100
90 END
100 PRINT M$R$,
110 LET M$ = CHR$ (13)
120 GOSUB 150"ROMAN NUMERALS DECODE given R$"
130 PRINT N;
140 RETURN
150 IF NOT C THEN GOSUB 250INITIALIZE
160 LET J = 0
170 LET N = 0
180 FOR I = LEN (R$) TO 1 STEP - 1
190 LET P = J
200 FOR J = 1 TO C
210 IF MID$ (C$,J,1) < > MID$ (R$,I,1) THEN NEXT J
220 IF J < = C THEN N = N + R(J) * ((J > = P) * 2 - 1)
230 NEXT I
240 RETURN
250 READ C$
260 LET C = LEN (C$)
270 DIM R(C)
280 FOR I = 0 TO C
290 READ R(I)
300 NEXT I
310 RETURN
320 DATA "IVXLCDM",0,1,5,10,50,100,500,1000</syntaxhighlight>
==={{header|BASIC256}}===
<syntaxhighlight lang="freebasic">function romToDec (roman$)
num = 0
prenum = 0
for i = length(roman$) to 1 step -1
x$ = mid(roman$, i, 1)
n = 0
if x$ = "M" then n = 1000
if x$ = "D" then n = 500
if x$ = "C" then n = 100
if x$ = "L" then n = 50
if x$ = "X" then n = 10
if x$ = "V" then n = 5
if x$ = "I" then n = 1

if n < preNum then num -= n else num += n
preNum = n
next i

return num
end function

#Testing
print "MCMXCIX = "; romToDec("MCMXCIX") #1999
print "MDCLXVI = "; romToDec("MDCLXVI") #1666
print "XXV = "; romToDec("XXV") #25
print "CMLIV = "; romToDec("CMLIV") #954
print "MMXI = "; romToDec("MMXI") #2011</syntaxhighlight>

==={{header|BBC BASIC}}===
<syntaxhighlight lang="bbcbasic"> PRINT "MCMXCIX", FNromandecode("MCMXCIX")
PRINT "MMXII", FNromandecode("MMXII")
PRINT "MDCLXVI", FNromandecode("MDCLXVI")
PRINT "MMMDCCCLXXXVIII", FNromandecode("MMMDCCCLXXXVIII")
END
DEF FNromandecode(roman$)
LOCAL i%, j%, p%, n%, r%()
DIM r%(7) : r%() = 0,1,5,10,50,100,500,1000
FOR i% = LEN(roman$) TO 1 STEP -1
j% = INSTR("IVXLCDM", MID$(roman$,i%,1))
IF j%=0 ERROR 100, "Invalid character"
IF j%>=p% n% += r%(j%) ELSE n% -= r%(j%)
p% = j%
NEXT
= n%</syntaxhighlight>
{{out}}
<pre>MCMXCIX 1999
MMXII 2012
MDCLXVI 1666
MMMDCCCLXXXVIII 3888</pre>

==={{header|Chipmunk Basic}}===
====Through IF-THEN statements====
{{works with|Chipmunk Basic|3.6.4}}
{{works with|Applesoft BASIC}}
{{works with|MSX_BASIC}}
{{works with|QBasic}}
<syntaxhighlight lang="qbasic">100 cls : rem 100 home for Applesoft BASIC
110 roman$ = "MCMXCIX" : print roman$,"=> "; : gosub 170 : print decimal '1999
120 roman$ = "XXV" : print roman$,"=> "; : gosub 170 : print decimal '25
130 roman$ = "CMLIV" : print roman$,"=> "; : gosub 170 : print decimal '954
140 roman$ = "MMXI" : print roman$,"=> "; : gosub 170 : print decimal '2011
150 end
160 rem Decode from roman
170 decimal = 0
180 predecimal = 0
190 for i = len(roman$) to 1 step -1
200 x$ = mid$(roman$,i,1)
210 if x$ = "M" then n = 1000 : goto 280
220 if x$ = "D" then n = 500 : goto 280
230 if x$ = "C" then n = 100 : goto 280
240 if x$ = "L" then n = 50 : goto 280
250 if x$ = "X" then n = 10 : goto 280
260 if x$ = "V" then n = 5 : goto 280
270 if x$ = "I" then n = 1
280 if n < predecimal then decimal = decimal-n
285 if n >= predecimal then decimal = decimal+n
290 predecimal = n
300 next i
310 return</syntaxhighlight>

====Through SELECT CASE statement====
{{works with|Chipmunk Basic|3.6.4}}
<syntaxhighlight lang="qbasic">100 cls
110 roman$ = "MCMXCIX" : print roman$,"=> "; : gosub 170 : print decimal '1999
120 roman$ = "XXV" : print roman$,"=> "; : gosub 170 : print decimal '25
130 roman$ = "CMLIV" : print roman$,"=> "; : gosub 170 : print decimal '954
140 roman$ = "MMXI" : print roman$,"=> "; : gosub 170 : print decimal '2011
150 end
160 rem Decode from roman
170 decimal = 0
180 predecimal = 0
190 for i = len(roman$) to 1 step -1
200 x$ = mid$(roman$,i,1)
210 select case x$
220 case "M" : n = 1000
230 case "D" : n = 500
240 case "C" : n = 100
250 case "L" : n = 50
260 case "X" : n = 10
270 case "V" : n = 5
280 case "I" : n = 1
290 case else : print "not a roman numeral" : end
300 end select
310 if n < predecimal then decimal = decimal-n else decimal = decimal+n
320 predecimal = n
330 next i
340 return</syntaxhighlight>

==={{header|FreeBASIC}}===
<syntaxhighlight lang="freebasic">' FB 1.05.0 Win64

Function romanDecode(roman As Const String) As Integer
If roman = "" Then Return 0 '' zero denotes invalid roman number
Dim roman1(0 To 2) As String = {"MMM", "MM", "M"}
Dim roman2(0 To 8) As String = {"CM", "DCCC", "DCC", "DC", "D", "CD", "CCC", "CC", "C"}
Dim roman3(0 To 8) As String = {"XC", "LXXX", "LXX", "LX", "L", "XL", "XXX", "XX", "X"}
Dim roman4(0 To 8) As String = {"IX", "VIII", "VII", "VI", "V", "IV", "III", "II", "I"}
Dim As Integer i, value = 0, length = 0
Dim r As String = UCase(roman)

For i = 0 To 2
If Left(r, Len(roman1(i))) = roman1(i) Then
value += 1000 * (3 - i)
length = Len(roman1(i))
r = Mid(r, length + 1)
length = 0
Exit For
End If
Next

For i = 0 To 8
If Left(r, Len(roman2(i))) = roman2(i) Then
value += 100 * (9 - i)
length = Len(roman2(i))
r = Mid(r, length + 1)
length = 0
Exit For
End If
Next

For i = 0 To 8
If Left(r, Len(roman3(i))) = roman3(i) Then
value += 10 * (9 - i)
length = Len(roman3(i))
r = Mid(r, length + 1)
length = 0
Exit For
End If
Next

For i = 0 To 8
If Left(r, Len(roman4(i))) = roman4(i) Then
value += 9 - i
length = Len(roman4(i))
Exit For
End If
Next
' Can't be a valid roman number if there are any characters left
If Len(r) > length Then Return 0
Return value
End Function

Dim a(2) As String = {"MCMXC", "MMVIII" , "MDCLXVI"}
For i As Integer = 0 To 2
Print a(i); Tab(8); " =>"; romanDecode(a(i))
Next

Print
Print "Press any key to quit"
Sleep</syntaxhighlight>
{{out}}
<pre>MCMXC => 1990
MMVIII => 2008
MDCLXVI => 1666</pre>

==={{header|FutureBasic}}===
<syntaxhighlight lang="futurebasic">window 1

local fn RomantoDecimal( roman as CFStringRef ) as long
long i, n, preNum = 0, num = 0
for i = len(roman) - 1 to 0 step -1
n = 0
select ( fn StringCharacterAtIndex( roman, i ) )
case _"M" : n = 1000
case _"D" : n = 500
case _"C" : n = 100
case _"L" : n = 50
case _"X" : n = 10
case _"V" : n = 5
case _"I" : n = 1
end select
if ( n < preNum ) then num = num - n else num = num + n
preNum = n
next
end fn = num

print @" MCMXC = "; fn RomantoDecimal( @"MCMXC" )
print @" MMVIII = "; fn RomantoDecimal( @"MMVIII" )
print @" MMXVI = "; fn RomantoDecimal( @"MMXVI" )
print @"MDCLXVI = "; fn RomantoDecimal( @"MDCLXVI" )
print @" MCMXIV = "; fn RomantoDecimal( @"MCMXIV" )
print @" DXIII = "; fn RomantoDecimal( @"DXIII" )
print @" M = "; fn RomantoDecimal( @"M" )
print @" DXIII = "; fn RomantoDecimal( @"DXIII" )
print @" XXXIII = "; fn RomantoDecimal( @"XXXIII" )

HandleEvents</syntaxhighlight>
{{out}}
<pre> MCMXC = 1990
MMVIII = 2008
MMXVI = 2016
MDCLXVI = 1666
MCMXIV = 1914
DXIII = 513
M = 1000
DXIII = 513
XXXIII = 33</pre>

==={{header|Gambas}}===
<syntaxhighlight lang="gambas">'This code will create a GUI Form and Objects and carry out the Roman Numeral convertion as you type
'The input is case insensitive
'A basic check for invalid charaters is made

hTextBox As TextBox 'To allow the creation of a TextBox
hValueBox As ValueBox 'To allow the creation of a ValueBox

Public Sub Form_Open() 'Form opens..

SetUpForm 'Go to the SetUpForm Routine
hTextBox.text = "MCMXC" 'Put a Roman numeral in the TextBox

End

Public Sub TextBoxInput_Change() 'Each time the TextBox text changes..
Dim cRomanN As Collection = ["M": 1000, "D": 500, "C": 100, "L": 50, "X": 10, "V": 5, "I": 1] 'Collection of nemerals e.g 'M' = 1000
Dim cMinus As Collection = ["IV": -2, "IX": -2, "XL": -20, "XC": - 20, "CD": -200, "CM": -200] 'Collection of the 'one less than' numbers e.g. 'IV' = 4
Dim sClean, sTemp As String 'Various string variables
Dim siCount As Short 'Counter
Dim iTotal As Integer 'Stores the total of the calculation

hTextBox.Text = UCase(hTextBox.Text) 'Make any text in the TextBox upper case

For siCount = 1 To Len(hTextBox.Text) 'Loop through each character in the TextBox
If InStr("MDCLXVI", Mid(hTextBox.Text, siCount, 1)) Then 'If a Roman numeral exists then..
sClean &= Mid(hTextBox.Text, siCount, 1) 'Put it in 'sClean' (Stops input of non Roman numerals)
End If
Next

hTextBox.Text = sClean 'Put the now clean text in the TextBox

For siCount = 1 To Len(hTextBox.Text) 'Loop through each character in the TextBox
iTotal += cRomanN[Mid(hTextBox.Text, siCount, 1)] 'Total up all the characters, note 'IX' will = 11 not 9
Next

For Each sTemp In cMinus 'Loop through each item in the cMinus Collection
If InStr(sClean, cMinus.Key) > 0 Then iTotal += Val(sTemp) 'If a 'Minus' value is in the string e.g. 'IX' which has been calculated at 11 subtract 2 = 9
Next

hValueBox.text = iTotal 'Display the total

End

Public Sub SetUpForm() 'Create the Objects for the Form
Dim hLabel1, hLabel2 As Label 'For 2 Labels

Me.height = 150 'Form Height
Me.Width = 300 'Form Width
Me.Padding = 20 'Form padding (border)
Me.Text = "Roman Numeral converter" 'Text in Form header
Me.Arrangement = Arrange.Vertical 'Form arrangement

hLabel1 = New Label(Me) 'Create a Label
hLabel1.Height = 21 'Label Height
hLabel1.expand = True 'Expand the Label
hLabel1.Text = "Enter a Roman numeral" 'Put text in the Label

hTextBox = New TextBox(Me) As "TextBoxInput" 'Set up a TextBox with an Event Label
hTextBox.Height = 21 'TextBox height
hTextBox.expand = True 'Expand the TextBox

hLabel2 = New Label(Me) 'Create a Label
hLabel2.Height = 21 'Label Height
hLabel2.expand = True 'Expand the Label
hLabel2.Text = "The decimal equivelent is: -" 'Put text in the Label

hValueBox = New ValueBox(Me) 'Create a ValueBox
hValueBox.Height = 21 'ValuBox Height
hValueBox.expand = True 'Expand the ValueBox
hValueBox.ReadOnly = True 'Set ValueBox to Read Only

End</syntaxhighlight>
'''[http://www.cogier.com/gambas/Roman%20Numeral%20converter.png Click here for image of running code]'''

==={{header|GW-BASIC}}===
The [[#Chipmunk_Basic|Chipmunk Basic]] [[#Through_IF-THEN_statements|through IF-THEN statements]] solution works without any changes.

==={{header|Liberty BASIC}}===
As Fortran & PureBasic.
<syntaxhighlight lang="lb"> print "MCMXCIX = "; romanDec( "MCMXCIX") '1999
print "MDCLXVI = "; romanDec( "MDCLXVI") '1666
print "XXV = "; romanDec( "XXV") '25
print "CMLIV = "; romanDec( "CMLIV") '954
print "MMXI = "; romanDec( "MMXI") '2011

end

function romanDec( roman$)
arabic =0
lastval =0

for i = len( roman$) to 1 step -1
select case upper$( mid$( roman$, i, 1))
case "M"
n = 1000
case "D"
n = 500
case "C"
n = 100
case "L"
n = 50
case "X"
n = 10
case "V"
n = 5
case "I"
n = 1
case else
n = 0
end select

if n <lastval then
arabic =arabic -n
else
arabic =arabic +n
end if

lastval =n
next

romanDec =arabic
end function</syntaxhighlight>
{{out}}
<pre>MCMXCIX = 1999
MDCLXVI = 1666
XXV = 25
CMLIV = 954
MMXI = 2011</pre>

==={{header|MSX Basic}}===
The [[#Chipmunk_Basic|Chipmunk Basic]] [[#Through_IF-THEN_statements|through IF-THEN statements]] solution works without any changes.

==={{header|PureBasic}}===
<syntaxhighlight lang="purebasic">Procedure romanDec(roman.s)
Protected i, n, lastval, arabic
For i = Len(roman) To 1 Step -1
Select UCase(Mid(roman, i, 1))
Case "M"
n = 1000
Case "D"
n = 500
Case "C"
n = 100
Case "L"
n = 50
Case "X"
n = 10
Case "V"
n = 5
Case "I"
n = 1
Default
n = 0
EndSelect
If (n < lastval)
arabic - n
Else
arabic + n
EndIf
lastval = n
Next
ProcedureReturn arabic
EndProcedure

If OpenConsole()
PrintN(Str(romanDec("MCMXCIX"))) ;1999
PrintN(Str(romanDec("MDCLXVI"))) ;1666
PrintN(Str(romanDec("XXV"))) ;25
PrintN(Str(romanDec("CMLIV"))) ;954
PrintN(Str(romanDec("MMXI"))) ;2011
Print(#CRLF$ + #CRLF$ + "Press ENTER to exit"): Input()
CloseConsole()
EndIf</syntaxhighlight>
{{out}}
<pre>1999
1666
25
954
2011</pre>

==={{header|QBasic}}===
<syntaxhighlight lang="qbasic">FUNCTION romToDec (roman$)
num = 0
prenum = 0
FOR i = LEN(roman$) TO 1 STEP -1
x$ = MID$(roman$, i, 1)
n = 0
IF x$ = "M" THEN n = 1000
IF x$ = "D" THEN n = 500
IF x$ = "C" THEN n = 100
IF x$ = "L" THEN n = 50
IF x$ = "X" THEN n = 10
IF x$ = "V" THEN n = 5
IF x$ = "I" THEN n = 1

IF n < preNum THEN num = num - n ELSE num = num + n
preNum = n
NEXT i

romToDec = num
END FUNCTION

!Testing
PRINT "MCMXCIX = "; romToDec("MCMXCIX") '1999
PRINT "MDCLXVI = "; romToDec("MDCLXVI") '1666
PRINT "XXV = "; romToDec("XXV") '25
PRINT "CMLIV = "; romToDec("CMLIV") '954
PRINT "MMXI = "; romToDec("MMXI") '2011</syntaxhighlight>

==={{header|QB64}}===
<syntaxhighlight lang="qb64">SCREEN _NEWIMAGE(400, 600, 32)


CLS


Main:
'------------------------------------------------
' CALLS THE romToDec FUNCTION WITH THE ROMAN
' NUMERALS AND RETURNS ITS DECIMAL EQUIVELENT.
'
PRINT "ROMAN NUMERAL TO DECIMAL CONVERSION"
PRINT: PRINT

PRINT "MDCCIV = "; romToDec("MDCCIV") '1704
PRINT "MCMXC = "; romToDec("MCMXC") '1990
PRINT "MMVIII = "; romToDec("MMVIII") '2008
PRINT "MDCLXVI = "; romToDec("MDCLXVI") '1666
PRINT: PRINT
PRINT "Here are other solutions not from the TASK:"
PRINT "MCMXCIX = "; romToDec("MCMXCIX") '1999
PRINT "XXV = "; romToDec("XXV") '25
PRINT "CMLIV = "; romToDec("CMLIV") '954
PRINT "MMXI = "; romToDec("MMXI") '2011
PRINT "MMIIIX = "; romToDec("MMIIIX") '2011
PRINT: PRINT
PRINT "2011 can be written either as MMXI or MMIIIX"
PRINT "With the IX = 9, MMIIIX is also 2011."
PRINT "2011 IS CORRECT (MM=2000 + II = 2 + IX = 9)"

END



FUNCTION romToDec (roman AS STRING)
'------------------------------------------------------
' FUNCTION THAT CONVERTS ANY ROMAN NUMERAL TO A DECIMAL
'
prenum = 0: num = 0
LN = LEN(roman)
FOR i = LN TO 1 STEP -1
x$ = MID$(roman, i, 1)
n = 1000
SELECT CASE x$
CASE "M": n = n / 1
CASE "D": n = n / 2
CASE "C": n = n / 10
CASE "L": n = n / 20
CASE "X": n = n / 100
CASE "V": n = n / 200
CASE "I": n = n / n
CASE ELSE: n = 0
END SELECT
IF n < prenum THEN num = num - n ELSE num = num + n
prenum = n
NEXT i

romToDec = num

END FUNCTION</syntaxhighlight>

==={{header|Run BASIC}}===
<syntaxhighlight lang="runbasic">print "MCMXCIX = "; romToDec( "MCMXCIX") '1999
print "MDCLXVI = "; romToDec( "MDCLXVI") '1666
print "XXV = "; romToDec( "XXV") '25
print "CMLIV = "; romToDec( "CMLIV") '954
print "MMXI = "; romToDec( "MMXI") '2011

function romToDec(roman$)
for i = len(roman$) to 1 step -1
x$ = mid$(roman$, i, 1)
n = 0
if x$ = "M" then n = 1000
if x$ = "D" then n = 500
if x$ = "C" then n = 100
if x$ = "L" then n = 50
if x$ = "X" then n = 10
if x$ = "V" then n = 5
if x$ = "I" then n = 1
if n < preNum then num = num - n else num = num + n
preNum = n
next
romToDec =num
end function</syntaxhighlight>

==={{header|TechBASIC}}===
<syntaxhighlight lang="techbasic">Main:
!------------------------------------------------
! CALLS THE romToDec FUNCTION WITH THE ROMAN
! NUMERALS AND RETURNS ITS DECIMAL EQUIVELENT.
!
PRINT "MCMXC = "; romToDec("MCMXC") !1990
PRINT "MMVIII = "; romToDec("MMVIII") !2008
PRINT "MDCLXVI = "; romToDec("MDCLXVI") !1666
PRINT:PRINT
PRINT "Here are other solutions not from the TASK:"
PRINT "MCMXCIX = "; romToDec("MCMXCIX") !1999
PRINT "XXV = "; romToDec("XXV") !25
PRINT "CMLIV = "; romToDec("CMLIV") !954
PRINT "MMXI = "; romToDec("MMXI") !2011
PRINT:PRINT
PRINT "Without error checking, this also is 2011, but is wrong"
PRINT "MMIIIX = "; romToDec("MMIIIX") !INVAID, 2011
STOP


FUNCTION romToDec(roman AS STRING) AS INTEGER
!------------------------------------------------------
! FUNCTION THAT CONVERTS ANY ROMAN NUMERAL TO A DECIMAL
!
prenum=0!num=0
ln=LEN(roman)
FOR i=ln TO 1 STEP -1
x$=MID(roman,i,1)
n=1000
SELECT CASE x$
CASE "M":n=n/1
CASE "D":n=n/2
CASE "C":n=n/10
CASE "L":n=n/20
CASE "X":n=n/100
CASE "V":n=n/200
CASE "I":n=n/n
CASE ELSE:n=0
END SELECT
IF n < preNum THEN num=num-n ELSE num=num+n
preNum=n
next i
romToDec=num

END FUNCTION</syntaxhighlight>
{{out}}
<pre>MCMXC = 1990
MMVIII = 2008
MDCLXVI = 1666


Here are other solutions not from the TASK:
MCMXCIX = 1999
XXV = 25
CMLIV = 954
MMXI = 2011


Without error checking, this also is 2011, but is wrong
MMIIIX = 2011</pre>

==={{header|TI-83 BASIC}}===
Using the Rom‣Dec function "real(21," from [http://www.detachedsolutions.com/omnicalc/ Omnicalc].
<syntaxhighlight lang="ti83b">PROGRAM:ROM2DEC
:Input Str1
:Disp real(21,Str1)</syntaxhighlight>

Using TI-83 BASIC
<syntaxhighlight lang="ti83b">PROGRAM:ROM2DEC
:Input "ROMAN:",Str1
:{1000,500,100,50,10,5,1}➞L1
:0➞P
:0➞Y
:For(I,length(Str1),1,-1)
:inString("MDCLXVI",sub(Str1,I,1))➞X
:If X≤0:Then
:Disp "BAD NUMBER"
:Stop
:End
:L1(x)➞N
:If N<P:Then
:Y–N➞Y
:Else
:Y+N➞Y
:End
:N➞P
:End
:Disp Y</syntaxhighlight>

==={{header|True BASIC}}===
<syntaxhighlight lang="qbasic">FUNCTION romtodec(roman$)
LET num = 0
LET prenum = 0
FOR i = len(roman$) to 1 step -1
LET x$ = (roman$)[i:i+1-1]
LET n = 0
IF x$ = "M" then LET n = 1000
IF x$ = "D" then LET n = 500
IF x$ = "C" then LET n = 100
IF x$ = "L" then LET n = 50
IF x$ = "X" then LET n = 10
IF x$ = "V" then LET n = 5
IF x$ = "I" then LET n = 1
IF n < prenum then LET num = num-n else LET num = num+n
LET prenum = n
NEXT i

LET romtodec = num
END FUNCTION

!Testing
PRINT "MCMXCIX = "; romToDec("MCMXCIX") !1999
PRINT "MDCLXVI = "; romToDec("MDCLXVI") !1666
PRINT "XXV = "; romToDec("XXV") !25
PRINT "CMLIV = "; romToDec("CMLIV") !954
PRINT "MMXI = "; romToDec("MMXI") !2011
END</syntaxhighlight>

==={{header|XBasic}}===
{{works with|Windows XBasic}}
<syntaxhighlight lang="qbasic">PROGRAM "romandec"
VERSION "0.0000"

DECLARE FUNCTION Entry ()
DECLARE FUNCTION romToDec (roman$)

FUNCTION Entry ()
PRINT "MCMXCIX = "; romToDec("MCMXCIX")
PRINT "MDCLXVI = "; romToDec("MDCLXVI")
PRINT "XXV = "; romToDec("XXV")
PRINT "CMLIV = "; romToDec("CMLIV")
PRINT "MMXI = "; romToDec("MMXI")
END FUNCTION

FUNCTION romToDec (roman$)
num = 0
prenum = 0
FOR i = LEN(roman$) TO 1 STEP -1
x$ = MID$(roman$, i, 1)
SELECT CASE x$
CASE "M" : n = 1000
CASE "D" : n = 500
CASE "C" : n = 100
CASE "L" : n = 50
CASE "X" : n = 10
CASE "V" : n = 5
CASE "I" : n = 1
END SELECT
IF n < prenum THEN num = num-n ELSE num = num+n
prenum = n
NEXT i

RETURN num
END FUNCTION
END PROGRAM</syntaxhighlight>

==={{header|Yabasic}}===
<syntaxhighlight lang="yabasic">romans$ = "MDCLXVI"
decmls$ = "1000,500,100,50,10,5,1"

sub romanDec(s$)
local i, n, prev, res, decmls$(1)
n = token(decmls$, decmls$(), ",")
for i = len(s$) to 1 step -1
n = val(decmls$(instr(romans$, mid$(s$, i, 1))))
if n < prev n = 0 - n
res = res + n
prev = n
next i
return res
end sub
? romanDec("MCMXCIX") // 1999
? romanDec("MDCLXVI") // 1666
? romanDec("XXV") // 25
? romanDec("XIX") // 19
? romanDec("XI") // 11
? romanDec("CMLIV") // 954
? romanDec("MMXI") // 2011
? romanDec("CD") // 400
? romanDec("MCMXC") // 1990
? romanDec("MMVIII") // 2008
? romanDec("MMIX") // 2009
? romanDec("MDCLXVI") // 1666
? romanDec("MMMDCCCLXXXVIII") // 3888</syntaxhighlight>

=={{header|Arturo}}==
<syntaxhighlight lang="rebol">syms: #[ M: 1000, D: 500, C: 100, L: 50, X: 10, V: 5, I: 1 ]

fromRoman: function [roman][
ret: 0
loop 0..(size roman)-2 'ch [
fst: roman\[ch]
snd: roman\[ch+1]
valueA: syms\[fst]
valueB: syms\[snd]

if? valueA < valueB -> ret: ret - valueA
else -> ret: ret + valueA
]
return ret + syms\[last roman]
]

loop ["MCMXC" "MMVIII" "MDCLXVI"] 'r -> print [r "->" fromRoman r]</syntaxhighlight>
{{out}}
<pre>MCMXC -> 1990
MMVIII -> 2008
MDCLXVI -> 1666</pre>

=={{header|AutoHotkey}}==
{{works with|AutoHotkey_L}}
<syntaxhighlight lang="ahk">Roman_Decode(str){
res := 0
Loop Parse, str
{
n := {M: 1000, D:500, C:100, L:50, X:10, V:5, I:1}[A_LoopField]
If ( n > OldN ) && OldN
res -= 2*OldN
res += n, oldN := n
}
return res
}

test = MCMXC|MMVIII|MDCLXVI
Loop Parse, test, |
res .= A_LoopField "`t= " Roman_Decode(A_LoopField) "`r`n"
clipboard := res</syntaxhighlight>
{{out}}
<pre>MCMXC = 1990
MMVIII = 2008
MDCLXVI = 1666</pre>

=={{header|AWK}}==
<syntaxhighlight lang="awk"># syntax: GAWK -f ROMAN_NUMERALS_DECODE.AWK
BEGIN {
leng = split("MCMXC MMVIII MDCLXVI",arr," ")
for (i=1; i<=leng; i++) {
n = arr[i]
printf("%s = %s\n",n,roman2arabic(n))
}
exit(0)
}
function roman2arabic(r, a,i,p,q,u,ua,una,unr) {
r = toupper(r)
unr = "MDCLXVI" # each Roman numeral in descending order
una = "1000 500 100 50 10 5 1" # and its Arabic equivalent
split(una,ua," ")
i = split(r,u,"")
a = ua[index(unr,u[i])]
while (--i) {
p = index(unr,u[i])
q = index(unr,u[i+1])
a += ua[p] * ((p>q) ? -1 : 1)
}
return( (a>0) ? a : "" )
}</syntaxhighlight>
{{out}}
<pre>MCMXC = 1990
MMVIII = 2008
MDCLXVI = 1666</pre>

=={{header|Batch File}}==
{{trans|Fortran}}
<syntaxhighlight lang="dos">@echo off
setlocal enabledelayedexpansion

::Testing...
call :toArabic MCMXC
echo MCMXC = !arabic!
call :toArabic MMVIII
echo MMVIII = !arabic!
call :toArabic MDCLXVI
echo MDCLXVI = !arabic!
call :toArabic CDXLIV
echo CDXLIV = !arabic!
call :toArabic XCIX
echo XCIX = !arabic!
pause>nul
exit/b 0

::The "function"...
:toArabic
set roman=%1
set arabic=
set lastval=
%== Alternative for counting the string length ==%
set leng=-1
for /l %%. in (0,1,1000) do set/a leng+=1&if "!roman:~%%.,1!"=="" goto break
:break
set /a last=!leng!-1
for /l %%i in (!last!,-1,0) do (
set n=0
if /i "!roman:~%%i,1!"=="M" set n=1000
if /i "!roman:~%%i,1!"=="D" set n=500
if /i "!roman:~%%i,1!"=="C" set n=100
if /i "!roman:~%%i,1!"=="L" set n=50
if /i "!roman:~%%i,1!"=="X" set n=10
if /i "!roman:~%%i,1!"=="V" set n=5
if /i "!roman:~%%i,1!"=="I" set n=1
if !n! lss !lastval! (
set /a arabic-=n
) else (
set /a arabic+=n
)
set lastval=!n!
)
goto :EOF</syntaxhighlight>
{{Out}}
<pre>MCMXC = 1990
MMVIII = 2008
MDCLXVI = 1666
CDXLIV = 444
XCIX = 99</pre>

=={{header|BCPL}}==
<syntaxhighlight lang="bcpl">get "libhdr"

let roman(s) = valof
$( let digit(ch) = valof
$( let ds = table 'm','d','c','l','x','v','i'
let vs = table 1000,500,100,50,10,5,1
for i=0 to 6
if ds!i=(ch|32) then resultis vs!i
resultis 0
$)
let acc = 0
for i=1 to s%0
$( let d = digit(s%i)
if d=0 then resultis 0
test i<s%0 & d<digit(s%(i+1))
do acc := acc-d
or acc := acc+d
$)
resultis acc
$)

let show(s) be writef("%S: %N*N", s, roman(s))
let start() be
$( show("MCMXC")
show("MDCLXVI")
show("MMVII")
show("MMXXI")
$)</syntaxhighlight>
{{out}}
<pre>MCMXC: 1990
MDCLXVI: 1666
MMVII: 2007
MMXXI: 2021</pre>

=={{header|BQN}}==

<syntaxhighlight lang="bqn">⟨ToArabic⇐A⟩ ← {
c ← "IVXLCDM" # Characters
v ← ⥊ (10⋆↕4) ×⌜ 1‿5 # Their values
A ⇐ +´∘(⊢ׯ1⋆<⟜«) v ⊏˜ c ⊐ ⊢
}</syntaxhighlight>

{{out|Example use}}
<syntaxhighlight lang="bqn"> ToArabic¨ "MCMXC"‿"MDCLXVI"‿"MMVII"‿"MMXXI"
⟨ 1990 1666 2007 2021 ⟩</syntaxhighlight>

=={{header|Bracmat}}==
{{trans|Icon and Unicon}}
<syntaxhighlight lang="bracmat"> ( unroman
= nbr,lastVal,val
. 0:?nbr:?lastVal
& @( low$!arg
: ?
%@?L
( ?
& (m.1000)
(d.500)
(c.100)
(l.50)
(x.10)
(v.5)
(i.1)
: ? (!L.?val) ?
& (!val:~>!lastVal|!val+-2*!lastVal)
+ !nbr
: ?nbr
& !val:?lastVal
& ~
)
)
| !nbr
)
& (M.1000)
(MCXI.1111)
(CMXI.911)
(MCM.1900)
(MCMXC.1990)
(MMVIII.2008)
(MMIX.2009)
(MCDXLIV.1444)
(MDCLXVI.1666)
(MMXII.2012)
: ?years
& (test=.out$(!arg unroman$!arg))
& ( !years
: ? (?L.?D) (?&test$!L&~)
| done
);</syntaxhighlight>
{{out}}
<pre>M 1000
MCXI 1111
CMXI 911
MCM 1900
MCMXC 1990
MMVIII 2008
MMIX 2009
MCDXLIV 1444
MDCLXVI 1666
MMXII 2012</pre>


=={{header|C}}==
=={{header|C}}==
Note: the code deliberately did not distinguish between "I", "J" or "U", "V", doing what Romans did for fun.
Note: the code deliberately did not distinguish between "I", "J" or "U", "V", doing what Romans did for fun.
<lang C>#include <stdio.h>
<syntaxhighlight lang="c">#include <stdio.h>


int digits[26] = { 0, 0, 100, 500, 0, 0, 0, 0, 1, 1, 0, 50, 1000, 0, 0, 0, 0, 0, 0, 0, 5, 5, 0, 10, 0, 0 };
int digits[26] = { 0, 0, 100, 500, 0, 0, 0, 0, 1, 1, 0, 50, 1000, 0, 0, 0, 0, 0, 0, 0, 5, 5, 0, 10, 0, 0 };
Line 66: Line 2,048:
#define VALUE(x) digits[(~0x20 & (x)) - 'A']
#define VALUE(x) digits[(~0x20 & (x)) - 'A']


int decode(char * roman)
int decode(const char * roman)
{
{
char *bigger;
const char *bigger;
int current;
int current;
int arabic = 0;
int arabic = 0;
Line 96: Line 2,078:
int main()
int main()
{
{
char * romans[] = { "MCmxC", "MMVIII", "MDClXVI", "MCXLUJ" };
const char * romans[] = { "MCmxC", "MMVIII", "MDClXVI", "MCXLUJ" };
int i;
int i;


Line 103: Line 2,085:


return 0;
return 0;
}</syntaxhighlight>
}

</lang>
=={{header|C sharp|C#}}==
<syntaxhighlight lang="csharp">using System;
using System.Collections.Generic;

namespace Roman
{
internal class Program
{
private static void Main(string[] args)
{
// Decode and print the numerals.
Console.WriteLine("{0}: {1}", "MCMXC", Decode("MCMXC"));
Console.WriteLine("{0}: {1}", "MMVIII", Decode("MMVIII"));
Console.WriteLine("{0}: {1}", "MDCLXVI", Decode("MDCLXVI"));
}

// Dictionary to hold our numerals and their values.
private static readonly Dictionary<char, int> RomanDictionary = new Dictionary<char, int>
{
{'I', 1},
{'V', 5},
{'X', 10},
{'L', 50},
{'C', 100},
{'D', 500},
{'M', 1000}
};

private static int Decode(string roman)
{
/* Make the input string upper-case,
* because the dictionary doesn't support lower-case characters. */
roman = roman.ToUpper();

/* total = the current total value that will be returned.
* minus = value to subtract from next numeral. */
int total = 0, minus = 0;

for (int i = 0; i < roman.Length; i++) // Iterate through characters.
{
// Get the value for the current numeral. Takes subtraction into account.
int thisNumeral = RomanDictionary[roman[i]] - minus;

/* Checks if this is the last character in the string, or if the current numeral
* is greater than or equal to the next numeral. If so, we will reset our minus
* variable and add the current numeral to the total value. Otherwise, we will
* subtract the current numeral from the next numeral, and continue. */
if (i >= roman.Length - 1 ||
thisNumeral + minus >= RomanDictionary[roman[i + 1]])
{
total += thisNumeral;
minus = 0;
}
else
{
minus = thisNumeral;
}
}

return total; // Return the total.
}
}
}</syntaxhighlight>
{{out}}
<pre>MCMXC: 1990
MMVIII: 2008
MDCLXVI: 1666
</pre>

=={{header|C++}}==
=={{header|C++}}==
<syntaxhighlight lang="cpp">
<lang cpp>#include <boost/array.hpp>
#include <iostream>
#include <exception>
#include <string>
#include <string>
#include <algorithm>
#include <iostream>
using namespace std;
#include <functional>


namespace Roman
class MySearch : public std::unary_function<std::pair<std::string , int> , bool> {
{
private : std::string value ;
int ToInt(char c)
public :
{
MySearch( const std::string & word ) : value( word ) { } ;
switch (c)
bool operator( )( const std::pair<std::string , int> &p ) const {
{
return p.first == value ;
case 'I': return 1;
}
case 'V': return 5;
} ;
case 'X': return 10;
case 'L': return 50;
case 'C': return 100;
case 'D': return 500;
case 'M': return 1000;
}
throw exception("Invalid character");
}


int RomanToInt( const std::string &roman ) {
int ToInt(const string& s)
{
typedef boost::array<std::pair<std::string , int> , 7> RomanType ;
int retval = 0, pvs = 0;
typedef boost::array<std::pair<std::string , int> ,7>::const_iterator PSI ;
for (auto pc = s.rbegin(); pc != s.rend(); ++pc)
static RomanType Romans = { std::make_pair( "I" , 1 ) ,
{
std::make_pair( "V" , 5 ) ,
const int inc = ToInt(*pc);
std::make_pair( "X" , 10 ) ,
retval += inc < pvs ? -inc : inc;
std::make_pair( "L" , 50 ) ,
pvs = inc;
std::make_pair( "C" , 100 ) ,
}
std::make_pair( "D" , 500 ) ,
return retval;
std::make_pair( "M" , 1000 ) } ;
}
int number = 0 ;
if ( roman.length( ) == 1 )
return std::find_if( Romans.begin( ) , Romans.end( ) , MySearch( roman ) )->second ;
else {
int i = 0 ;
while ( i < roman.length( ) ) {// look for all the letters in the array
int pos1 = std::find_if( Romans.begin( ) , Romans.end( ) , MySearch( roman.substr( i , 1 ) ))->second ;
int pos2 = std::find_if( Romans.begin( ) , Romans.end( ) , MySearch( roman.substr( i + 1 , 1 ) ))->second ;
if ( pos2 > pos1 ) {
number += pos2 - pos1 ;
i += 2 ;
}
else {
number += pos1 ;
i += 1 ;
}
}
}
return number ;
}
}


int main( ) {
int main(int argc, char* argv[])
{
std::cout << "MCMXC in Roman numerals is " << RomanToInt( "MCMXC" ) << " in Arabic!\n" ;
try
std::cout << "And MDCLXVI for the Romans is " << RomanToInt( "MDCLXVI" ) << " in better known Arabic figures!\n" ;
{
return 0 ;
cout << "MCMXC = " << Roman::ToInt("MCMXC") << "\n";
}</lang>
cout << "MMVIII = " << Roman::ToInt("MMVIII") << "\n";
Output:
cout << "MDCLXVI = " << Roman::ToInt("MDCLXVI") << "\n";
<PRE>MCMXC in Roman numerals is 1990 in Arabic!
}
And MDCLXVI for the Romans is 1666 in better known Arabic figures!
catch (exception& e)
</PRE>
{
cerr << e.what();
return -1;
}
return 0;
}
</syntaxhighlight>
{{out}}
<PRE>MCMXC = 1990
MMVIII = 2008
MDCLXVI = 1666</PRE>

=={{header|Ceylon}}==
<syntaxhighlight lang="ceylon">shared void run() {
value numerals = map {
'I' -> 1,
'V' -> 5,
'X' -> 10,
'L' -> 50,
'C' -> 100,
'D' -> 500,
'M' -> 1000
};
function toHindu(String roman) {
variable value total = 0;
for(i->c in roman.indexed) {
assert(exists currentValue = numerals[c]);
/* Look at the next letter to see if we're looking
at a IV or CM or whatever. If so subtract the
current number from the total. */
if(exists next = roman[i + 1],
exists nextValue = numerals[next],
currentValue < nextValue) {
total -= currentValue;
} else {
total += currentValue;
}
}
return total;
}
assert(toHindu("I") == 1);
assert(toHindu("II") == 2);
assert(toHindu("IV") == 4);
assert(toHindu("MDCLXVI") == 1666);
assert(toHindu("MCMXC") == 1990);
assert(toHindu("MMVIII") == 2008);
}</syntaxhighlight>

=={{header|Clojure}}==
<syntaxhighlight lang="clojure">
;; Incorporated some improvements from the alternative implementation below
(defn ro2ar [r]
(->> (reverse (.toUpperCase r))
(map {\M 1000 \D 500 \C 100 \L 50 \X 10 \V 5 \I 1})
(partition-by identity)
(map (partial apply +))
(reduce #(if (< %1 %2) (+ %1 %2) (- %1 %2)))))

;; alternative
(def numerals { \I 1, \V 5, \X 10, \L 50, \C 100, \D 500, \M 1000})
(defn from-roman [s]
(->> s .toUpperCase
(map numerals)
(reduce (fn [[sum lastv] curr] [(+ sum curr (if (< lastv curr) (* -2 lastv) 0)) curr]) [0,0])
first))</syntaxhighlight>

{{out}}
<pre>(map ro2ar ["MDCLXVI" "MMMCMXCIX" "XLVIII" "MMVIII"])
(1666 3999 48 2008)</pre>

=={{header|CLU}}==
<syntaxhighlight lang="clu">roman = cluster is decode
rep = null
digit_value = proc (c: char) returns (int) signals (invalid)
if c < 'a' then c := char$i2c(char$c2i(c) + 32) end
if c = 'm' then return(1000)
elseif c = 'd' then return(500)
elseif c = 'c' then return(100)
elseif c = 'l' then return(50)
elseif c = 'x' then return(10)
elseif c = 'v' then return(5)
elseif c = 'i' then return(1)
else signal invalid
end
end digit_value
decode = proc (s: string) returns (int) signals (invalid)
acc: int := 0
for i: int in int$from_to(1, string$size(s)) do
d: int := digit_value(s[i])
if i < string$size(s) cand d < digit_value(s[i+1]) then
acc := acc - d
else
acc := acc + d
end
end resignal invalid
return(acc)
end decode
end roman

start_up = proc ()
po: stream := stream$primary_output()
tests: array[string] := array[string]$
["MCMXC", "mdclxvi", "MmViI", "mmXXi", "INVALID"]
for test: string in array[string]$elements(tests) do
stream$puts(po, test || ": ")
stream$putl(po, int$unparse(roman$decode(test))) except when invalid:
stream$putl(po, "not a valid Roman numeral!")
end
end
end start_up</syntaxhighlight>
{{out}}
<pre>MCMXC: 1990
mdclxvi: 1666
MmViI: 2007
mmXXi: 2021
INVALID: not a valid Roman numeral!</pre>

=={{header|COBOL}}==
<syntaxhighlight lang="cobol">
IDENTIFICATION DIVISION.
PROGRAM-ID. UNROMAN.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 filler.
03 i pic 9(02) comp.
03 j pic 9(02) comp.
03 k pic 9(02) comp.
03 l pic 9(02) comp.
01 inp-roman.
03 inp-rom-ch pic x(01) occurs 20 times.
01 inp-roman-digits.
03 inp-rom-digit pic 9(01) occurs 20 times.
01 ws-search-idx pic 9(02) comp.
01 ws-tbl-table-def.
03 filler pic x(05) value '1000M'.
03 filler pic x(05) value '0500D'.
03 filler pic x(05) value '0100C'.
03 filler pic x(05) value '0050L'.
03 filler pic x(05) value '0010X'.
03 filler pic x(05) value '0005V'.
03 filler pic x(05) value '0001I'.
01 filler redefines ws-tbl-table-def.
03 ws-tbl-roman occurs 07 times indexed by rx.
05 ws-tbl-rom-val pic 9(04).
05 ws-tbl-rom-ch pic x(01).
01 ws-number pic s9(05) value 0.
01 ws-number-pic pic zzzz9-.

PROCEDURE DIVISION.
accept inp-roman
perform
until inp-roman = ' '
move zeroes to inp-roman-digits
perform
varying i from 1 by +1 until inp-rom-ch (i) = ' '
set rx to 1
search ws-tbl-roman
at end
move 0 to inp-rom-digit (i)
when ws-tbl-rom-ch (rx) = inp-rom-ch (i)
set inp-rom-digit (i) to rx
end-search
end-perform
compute l = i - 1
move 0 to ws-number
perform
varying i from 1 by +1
until i > l or inp-rom-digit (i) = 0
compute j = inp-rom-digit (i)
compute k = inp-rom-digit (i + 1)
if ws-tbl-rom-val (k)
> ws-tbl-rom-val (j)
compute ws-number
= ws-number
- ws-tbl-rom-val (j)
else
compute ws-number
= ws-number
+ ws-tbl-rom-val (j)
end-if
end-perform
move ws-number to ws-number-pic
display '----------'
display 'roman=' inp-roman
display 'arabic=' ws-number-pic
if i < l or ws-number = 0
display 'invalid/incomplete roman numeral at pos 'i
' found ' inp-rom-ch (i)
end-if
accept inp-roman
end-perform
stop run
.
END PROGRAM UNROMAN.
</syntaxhighlight>
{{out}} input was supplied via STDIN
<pre>
----------
roman=MCMLXXXVIII
arabic= 1988
----------
roman=MIX
arabic= 1009
----------
roman=MDCCCLXXXVII
arabic= 1887
----------
roman=IX
arabic= 9
----------
roman=MMMDCCCLXXXVIII
arabic= 3888
----------
roman=K
arabic= 0
invalid/incomplete roman numeral at pos 01 found K
----------
roman=MIXT
arabic= 1009
invalid/incomplete roman numeral at pos 04 found T
----------
roman=MCMB
arabic= 1900
invalid/incomplete roman numeral at pos 04 found B
</pre>

=={{header|CoffeeScript}}==
<syntaxhighlight lang="coffeescript">roman_to_demical = (s) ->
# s is well-formed Roman Numeral >= I
numbers =
M: 1000
D: 500
C: 100
L: 50
X: 10
V: 5
I: 1

result = 0
for c in s
num = numbers[c]
result += num
if old_num < num
# If old_num exists and is less than num, then
# we need to subtract it twice, once because we
# have already added it on the last pass, and twice
# to conform to the Roman convention that XC = 90,
# not 110.
result -= 2 * old_num
old_num = num
result
tests =
IV: 4
XLII: 42
MCMXC: 1990
MMVIII: 2008
MDCLXVI: 1666

for roman, expected of tests
dec = roman_to_demical(roman)
console.log "error" if dec != expected
console.log "#{roman} = #{dec}"</syntaxhighlight>

=={{header|Common Lisp}}==
<syntaxhighlight lang="lisp">
(defun mapcn (chars nums string)
(loop as char across string as i = (position char chars) collect (and i (nth i nums))))

(defun parse-roman (R)
(loop with nums = (mapcn "IVXLCDM" '(1 5 10 50 100 500 1000) R)
as (A B) on nums if A sum (if (and B (< A B)) (- A) A)))
</syntaxhighlight>

Description:

<pre>Mapcn is a function to map characters to numbers. It uses the mapping between its first two arguments, chars and nums,
to map its 3rd argument, string, to a list of numbers. If a character of string is missing from chars, its number will be
nil.

Parse-roman uses mapcn to map R to a list of numbers, then iterates that list with A and B, adding A to the total whenever
it's not less than B, and subtracting it when it is. If A is nil, it's skipped. Such as when the character is not Roman.
If B is nil, A is added and not subtracted. Such as at the end of the list, or when a non-Roman character, such as a space,
is embedded in the Roman.</pre>

Test code:

<syntaxhighlight lang="lisp">(dolist (r '("MCMXC" "MDCLXVI" "MMVIII"))
(format t "~a:~10t~d~%" r (parse-roman r)))</syntaxhighlight>
{{out}}
<pre>MCMXC: 1990
MDCLXVI: 1666
MMVIII: 2008</pre>

=={{header|Cowgol}}==

<syntaxhighlight lang="cowgol">include "cowgol.coh";
include "argv.coh";

# Decode the Roman numeral in the given string.
# Returns 0 if the string does not contain a valid Roman numeral.
sub romanToDecimal(str: [uint8]): (rslt: uint16) is
# Look up a Roman digit
sub digit(char: uint8): (val: uint16) is
# Definition of Roman numerals
record RomanDigit is
char: uint8;
value: uint16;
end record;
var digits: RomanDigit[] := {
{'I',1}, {'V',5}, {'X',10}, {'L',50},
{'C',100}, {'D',500}, {'M',1000}
};
char := char & ~32; # make uppercase
# Look up given digit
var i: @indexof digits := 0;
while i < @sizeof digits loop
val := digits[i].value;
if digits[i].char == char then
return;
end if;
i := i + 1;
end loop;
val := 0;
end sub;
rslt := 0;
while [str] != 0 loop
var cur := digit([str]); # get value of current digit
if cur == 0 then rslt := 0; return; end if; # stop when invalid
str := @next str;
if digit([str]) > cur then
# a digit followed by a larger digit should be subtracted from
# the total
rslt := rslt - cur;
else
rslt := rslt + cur;
end if;
end loop;
end sub;

# Read a Roman numeral from the command line and print its output
ArgvInit();
var argmt := ArgvNext();
if argmt == (0 as [uint8]) then
# No argument
print("No argument\n");
ExitWithError();
end if;

print_i16(romanToDecimal(argmt));
print_nl();</syntaxhighlight>

{{out}}
<pre>$ ./romandec.386 MCMXC
1990
$ ./romandec.386 MDCLXVI
1666
$ ./romandec.386 MMVII
2007</pre>


=={{header|D}}==
=={{header|D}}==
<lang d>import std.regex, std.algorithm;
<syntaxhighlight lang="d">import std.regex, std.algorithm;


int toArabic(string s) {
int toArabic(in string s) /*pure nothrow*/ {
static const weights = [1000, 900, 500, 400, 100, 90, 50,
static immutable weights = [1000, 900, 500, 400, 100,
40, 10, 9, 5, 4, 1];
90, 50, 40, 10, 9, 5, 4, 1];
static /*const*/ symbols = ["M","CM","D","CD","C","XC",
static immutable symbols = ["M","CM","D","CD","C","XC",
"L","XL","X","IX","V","IV","I"];
"L","XL","X","IX","V","IV","I"];

int arabic;
int arabic;
foreach (m; match(s, "CM|CD|XC|XL|IX|IV|[MDCLXVI]"))
foreach (m; s.matchAll("CM|CD|XC|XL|IX|IV|[MDCLXVI]".regex))
arabic += weights[symbols.countUntil(m.hit)];
arabic += weights[symbols.countUntil(m.hit)];

return arabic;
return arabic;
}
}


void main() {
void main() {
assert(toArabic("MCMXC") == 1990);
assert("MCMXC".toArabic == 1990);
assert(toArabic("MMVIII") == 2008);
assert("MMVIII".toArabic == 2008);
assert(toArabic("MDCLXVI") == 1666);
assert("MDCLXVI".toArabic == 1666);
}</lang>
}</syntaxhighlight>
Alternative version:
Alternative more functional version:
<lang d>import std.regex, std.algorithm;
<syntaxhighlight lang="d">import std.regex, std.algorithm;

immutable uint[string] w2s;


pure nothrow static this() {
int[string] w2s;
w2s = ["IX": 9, "C": 100, "D": 500, "CM": 900, "I": 1,
static this() {
w2s = ["IX": 9, "C": 100, "D": 500, "CM": 900, "I": 1,
"XC": 90, "M": 1000, "L": 50, "CD": 400, "XL": 40,
"XC": 90, "M": 1000, "L": 50, "CD": 400, "XL": 40,
"V": 5, "X": 10, "IV": 4];
"V": 5, "X": 10, "IV": 4];
}
}


int toArabic(string s) {
uint toArabic(in string s) /*pure nothrow*/ @safe /*@nogc*/ {
return s
auto ms = match(s, "CM|CD|XC|XL|IX|IV|[MDCLXVI]");
.matchAll("CM|CD|XC|XL|IX|IV|[MDCLXVI]".regex)
return reduce!q{ a + b }(map!((m){ return w2s[m.hit]; })(ms));
.map!(m => w2s[m.hit])
.sum;
}
}


void main() {
void main() {
assert(toArabic("MCMXC") == 1990);
assert("MCMXC".toArabic == 1990);
assert(toArabic("MMVIII") == 2008);
assert("MMVIII".toArabic == 2008);
assert(toArabic("MDCLXVI") == 1666);
assert("MDCLXVI".toArabic == 1666);
}</lang>
}</syntaxhighlight>


=={{header|Delphi}}==
=={{header|Delphi}}/{{header|Pascal}}==
<lang delphi>program RomanNumeralsDecode;
<syntaxhighlight lang="delphi">program RomanNumeralsDecode;


{$APPTYPE CONSOLE}
{$APPTYPE CONSOLE}
Line 247: Line 2,668:
Writeln(RomanToInteger('MMVIII')); // 2008
Writeln(RomanToInteger('MMVIII')); // 2008
Writeln(RomanToInteger('MDCLXVI')); // 1666
Writeln(RomanToInteger('MDCLXVI')); // 1666
end.</lang>
end.</syntaxhighlight>

=={{header|EasyLang}}==
<syntaxhighlight lang="text">
func rom2dec rom$ .
symbols$[] = [ "M" "D" "C" "L" "X" "V" "I" ]
values[] = [ 1000 500 100 50 10 5 1 ]
val = 0
for dig$ in strchars rom$
for i = 1 to len symbols$[]
if symbols$[i] = dig$
v = values[i]
.
.
val += v
if oldv < v
val -= 2 * oldv
.
oldv = v
.
return val
.
print rom2dec "MCMXC"
print rom2dec "MMVIII"
print rom2dec "MDCLXVI"
</syntaxhighlight>

=={{header|ECL}}==
The best declarative approach:
<syntaxhighlight lang="ecl">
MapChar(STRING1 c) := CASE(c,'M'=>1000,'D'=>500,'C'=>100,'L'=>50,'X'=>10,'V'=>5,'I'=>1,0);

RomanDecode(STRING s) := FUNCTION
dsS := DATASET([{s}],{STRING Inp});
R := { INTEGER2 i; };

R Trans1(dsS le,INTEGER pos) := TRANSFORM
SELF.i := MapChar(le.Inp[pos]) * IF ( MapChar(le.Inp[pos]) < MapChar(le.Inp[pos+1]), -1, 1 );
END;
RETURN SUM(NORMALIZE(dsS,LENGTH(TRIM(s)),Trans1(LEFT,COUNTER)),i);
END;

RomanDecode('MCMLIV'); //1954
RomanDecode('MCMXC'); //1990
RomanDecode('MMVIII'); //2008
RomanDecode('MDCLXVI'); //1666
RomanDecode('MDLXVI'); //1566</syntaxhighlight>
Here's an alternative that emulates the wat procedural code would approach the problem:
<syntaxhighlight lang="ecl">IMPORT STD;
RomanDecode(STRING s) := FUNCTION
SetWeights := [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];
SetSymbols := ['M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I'];
ProcessRec := RECORD
UNSIGNED val;
STRING Roman;
END;
dsSymbols := DATASET(13,TRANSFORM(ProcessRec,SELF.Roman := s, SELF := []));
RECORDOF(dsSymbols) XF(dsSymbols L, dsSymbols R, INTEGER C) := TRANSFORM
ThisRoman := IF(C=1,R.Roman,L.Roman);
IsDone := ThisRoman = '';
Repeatable := C IN [1,5,9,13];
SymSize := IF(C % 2 = 0, 2, 1);
IsNext := STD.Str.StartsWith(ThisRoman,SetSymbols[C]);
SymLen := IF(IsNext,
IF(NOT Repeatable,
SymSize,
MAP(NOT IsDone AND ThisRoman[1] = ThisRoman[2] AND ThisRoman[1] = ThisRoman[3] => 3,
NOT IsDone AND ThisRoman[1] = ThisRoman[2] => 2,
NOT IsDone => 1,
0)),
0);

SymbolWeight(STRING s) := IF(NOT Repeatable,
SetWeights[C],
CHOOSE(LENGTH(s),SetWeights[C],SetWeights[C]*2,SetWeights[C]*3,0));

SELF.Roman := IF(IsDone,ThisRoman,ThisRoman[SymLen+1..]);
SELF.val := IF(IsDone,L.val,L.Val + IF(IsNext,SymbolWeight(ThisRoman[1..SymLen]),0));
END;
i := ITERATE(dsSymbols,XF(LEFT,RIGHT,COUNTER));
RETURN i[13].val;
END;

RomanDecode('MCMLIV'); //1954
RomanDecode('MCMXC'); //1990
RomanDecode('MMVIII'); //2008
RomanDecode('MDCLXVI'); //1666
RomanDecode('MDLXVI'); //1566</syntaxhighlight>

=={{header|Eiffel}}==

This solution is case insensitive. It performs no input validation other than checking that all Roman digits in the input string are one of <tt>M</tt>, <tt>D</tt>, <tt>C</tt>, <tt>L</tt>, <tt>X</tt>, <tt>V</tt>, and <tt>I</tt>.

<syntaxhighlight lang="eiffel">class
APPLICATION

create
make

feature {NONE} -- Initialization

make
local
numbers: ARRAY [STRING]
do
numbers := <<"MCMXC", "MMVIII", "MDCLXVI",
-- 1990 2008 1666
"MMMCLIX", "MCMLXXVII", "MMX">>
-- 3159 1977 2010
across numbers as n loop
print (n.item +
" in Roman numerals is " +
roman_to_decimal (n.item).out +
" in decimal Arabic numerals.")
print ("%N")
end
end

feature -- Roman numerals

roman_to_decimal (a_str: STRING): INTEGER
-- Decimal representation of Roman numeral `a_str'
require
is_roman (a_str)
local
l_pos: INTEGER
cur: INTEGER -- Value of the digit read in the current iteration
prev: INTEGER -- Value of the digit read in the previous iteration
do
from
l_pos := 0
Result := 0
prev := 1 + max_digit_value
until
l_pos = a_str.count
loop
l_pos := l_pos + 1
cur := roman_digit_to_decimal (a_str.at (l_pos))
if cur <= prev then
-- Add nonincreasing digit
Result := Result + cur
else
-- Subtract previous digit from increasing digit
Result := Result - prev + (cur - prev)
end
prev := cur
end
ensure
Result >= 0
end

is_roman (a_string: STRING): BOOLEAN
-- Is `a_string' a valid sequence of Roman digits?
do
Result := across a_string as c all is_roman_digit (c.item) end
end

feature {NONE} -- Implementation

max_digit_value: INTEGER = 1000

is_roman_digit (a_char: CHARACTER): BOOLEAN
-- Is `a_char' a valid Roman digit?
local
l_char: CHARACTER
do
l_char := a_char.as_upper
inspect l_char
when 'I', 'V', 'X', 'L', 'C', 'D', 'M' then
Result := True
else
Result := False
end
end

roman_digit_to_decimal (a_char: CHARACTER): INTEGER
-- Decimal representation of Roman digit `a_char'
require
is_roman_digit (a_char)
local
l_char: CHARACTER
do
l_char := a_char.as_upper
inspect l_char
when 'I' then
Result := 1
when 'V' then
Result := 5
when 'X' then
Result := 10
when 'L' then
Result := 50
when 'C' then
Result := 100
when 'D' then
Result := 500
when 'M' then
Result := 1000
end
ensure
Result > 0
end

end</syntaxhighlight>

=={{header|Elena}}==
ELENA 6.x :
<syntaxhighlight lang="elena">import extensions;
import system'collections;
import system'routines;
import system'culture;
static RomanDictionary = Dictionary.new()
.setAt("I".toChar(), 1)
.setAt("V".toChar(), 5)
.setAt("X".toChar(), 10)
.setAt("L".toChar(), 50)
.setAt("C".toChar(), 100)
.setAt("D".toChar(), 500)
.setAt("M".toChar(), 1000);
extension op : String
{
toRomanInt()
{
var minus := 0;
var s := self.toUpper();
var total := 0;
for(int i := 0; i < s.Length; i += 1)
{
var thisNumeral := RomanDictionary[s[i]] - minus;
if (i >= s.Length - 1 || thisNumeral + minus >= RomanDictionary[s[i + 1]])
{
total += thisNumeral;
minus := 0
}
else
{
minus := thisNumeral
}
};
^ total
}
}
public program()
{
console.printLine("MCMXC: ", "MCMXC".toRomanInt());
console.printLine("MMVIII: ", "MMVIII".toRomanInt());
console.printLine("MDCLXVI:", "MDCLXVI".toRomanInt())
}</syntaxhighlight>
{{out}}
<pre>
MCMXC: 1990
MMVIII: 2008
MDCLXVI:1666
</pre>

=={{header|Elixir}}==
{{trans|Erlang}}
<syntaxhighlight lang="elixir">defmodule Roman_numeral do
def decode([]), do: 0
def decode([x]), do: to_value(x)
def decode([h1, h2 | rest]) do
case {to_value(h1), to_value(h2)} do
{v1, v2} when v1 < v2 -> v2 - v1 + decode(rest)
{v1, _} -> v1 + decode([h2 | rest])
end
end
defp to_value(?M), do: 1000
defp to_value(?D), do: 500
defp to_value(?C), do: 100
defp to_value(?L), do: 50
defp to_value(?X), do: 10
defp to_value(?V), do: 5
defp to_value(?I), do: 1
end

Enum.each(['MCMXC', 'MMVIII', 'MDCLXVI', 'IIIID'], fn clist ->
IO.puts "#{clist}\t: #{Roman_numeral.decode(clist)}"
end)</syntaxhighlight>

{{out}}
<pre>
MCMXC : 1990
MMVIII : 2008
MDCLXVI : 1666
</pre>

=={{header|Emacs Lisp}}==
<syntaxhighlight lang="lisp">(defun ro2ar (RN)
"Translate a roman number RN into arabic number.
Its argument RN is wether a symbol, wether a list.
Returns the arabic number. (ro2ar 'C) gives 100,
(ro2ar '(X X I V)) gives 24"
(cond
((eq RN 'M) 1000)
((eq RN 'D) 500)
((eq RN 'C) 100)
((eq RN 'L) 50)
((eq RN 'X) 10)
((eq RN 'V) 5)
((eq RN 'I) 1)
((null (cdr RN)) (ro2ar (car RN))) ;; stop recursion
((< (ro2ar (car RN)) (ro2ar (car (cdr RN)))) (- (ro2ar (cdr RN)) (ro2ar (car RN)))) ;; "IV" -> 5-1=4
(t (+ (ro2ar (car RN)) (ro2ar (cdr RN)))))) ;; "VI" -> 5+1=6</syntaxhighlight>

{{out}}

(ro2ar '(M D C L X V I)) ;=> 1666

=={{header|Erlang}}==
Putting the character X into a list, [X], creates a string with a single character.

<syntaxhighlight lang="erlang">
-module( roman_numerals ).

-export( [decode_from_string/1]).

to_value($M) -> 1000;
to_value($D) -> 500;
to_value($C) -> 100;
to_value($L) -> 50;
to_value($X) -> 10;
to_value($V) -> 5;
to_value($I) -> 1.

decode_from_string([]) -> 0;
decode_from_string([H1]) -> to_value(H1);
decode_from_string([H1, H2 |Rest]) ->
case {to_value(H1), to_value(H2)} of
{V1, V2} when V1 < V2 -> V2 - V1 + decode_from_string(Rest);
{V1, V1} -> V1 + V1 + decode_from_string(Rest);
{V1, _} -> V1 + decode_from_string([H2|Rest])
end.
</syntaxhighlight>

{{out}}
<pre>
10> roman_numerals:decode_from_string("MCMXC").
1990
11> roman_numerals:decode_from_string("MMVIII").
2008
12> roman_numerals:decode_from_string("MDCLXVI").
1666
</pre>

=={{header|ERRE}}==
<syntaxhighlight lang="erre">
PROGRAM ROMAN2ARAB

DIM R%[7]

PROCEDURE TOARAB(ROMAN$->ANS%)
LOCAL I%,J%,P%,N%
FOR I%=LEN(ROMAN$) TO 1 STEP -1 DO
J%=INSTR("IVXLCDM",MID$(ROMAN$,I%,1))
IF J%=0 THEN
ANS%=-9999 ! illegal character
EXIT PROCEDURE
END IF
IF J%>=P% THEN
N%+=R%[J%]
ELSE
N%-=R%[J%]
END IF
P%=J%
END FOR
ANS%=N%
END PROCEDURE

BEGIN
R%[]=(0,1,5,10,50,100,500,1000)
TOARAB("MCMXCIX"->ANS%) PRINT(ANS%)
TOARAB("MMXII"->ANS%) PRINT(ANS%)
TOARAB("MDCLXVI"->ANS%) PRINT(ANS%)
TOARAB("MMMDCCCLXXXVIII"->ANS%) PRINT(ANS%)
END PROGRAM
</syntaxhighlight>
If the answer is -9999, roman number is illegal.


=={{header|Euphoria}}==
=={{header|Euphoria}}==
{{trans|PureBasic}}
{{trans|PureBasic}}
<lang euphoria>constant symbols = "MDCLXVI", weights = {1000,500,100,50,10,5,1}
<syntaxhighlight lang="euphoria">constant symbols = "MDCLXVI", weights = {1000,500,100,50,10,5,1}
function romanDec(sequence roman)
function romanDec(sequence roman)
integer n, lastval, arabic
integer n, lastval, arabic
Line 275: Line 3,080:
? romanDec("XXV")
? romanDec("XXV")
? romanDec("CMLIV")
? romanDec("CMLIV")
? romanDec("MMXI")</lang>
? romanDec("MMXI")</syntaxhighlight>
{{out}}

Output:
<pre>1999
<pre>1999
1666
1666
Line 283: Line 3,087:
954
954
2011</pre>
2011</pre>

=={{header|F Sharp|F#}}==
This implementation uses tail recursion. The accumulator (arabic) and the last roman digit (lastval) are recursively passed as each element of the list is consumed.
<syntaxhighlight lang="fsharp">let decimal_of_roman roman =
let rec convert arabic lastval = function
| head::tail ->
let n = match head with
| 'M' | 'm' -> 1000
| 'D' | 'd' -> 500
| 'C' | 'c' -> 100
| 'L' | 'l' -> 50
| 'X' | 'x' -> 10
| 'V' | 'v' -> 5
| 'I' | 'i' -> 1
| _ -> 0
let op = if n > lastval then (-) else (+)
convert (op arabic lastval) n tail
| _ -> arabic + lastval
convert 0 0 (Seq.toList roman)
;;</syntaxhighlight>

Here is an alternative implementation that uses Seq(uence).fold. It threads a Tuple of the state (accumulator, last roman digit) through the list of characters.
<syntaxhighlight lang="fsharp">let decimal_of_roman roman =
let convert (arabic,lastval) c =
let n = match c with
| 'M' | 'm' -> 1000
| 'D' | 'd' -> 500
| 'C' | 'c' -> 100
| 'L' | 'l' -> 50
| 'X' | 'x' -> 10
| 'V' | 'v' -> 5
| 'I' | 'i' -> 1
| _ -> 0
let op = if n > lastval then (-) else (+)
(op arabic lastval, n)
let (arabic, lastval) = Seq.fold convert (0,0) roman
arabic + lastval
;;</syntaxhighlight>

Test code:
<syntaxhighlight lang="fsharp">let tests = ["MCMXC"; "MMVIII"; "MDCLXVII"; "MMMCLIX"; "MCMLXXVII"; "MMX"]
for test in tests do Printf.printf "%s: %d\n" test (decimal_of_roman test)
;;</syntaxhighlight>

{{out}}
<pre>MCMXC: 1990
MMVIII: 2008
MDCLXVII: 1667
MMMCLIX: 3159
MCMLXXVII: 1977
MMX: 2010</pre>

=={{header|Factor}}==
A roman numeral library ships with Factor.
<syntaxhighlight lang="factor">USE: roman
( scratchpad ) "MMMCCCXXXIII" roman> .
3333</syntaxhighlight>

Implementation for decoding:

<syntaxhighlight lang="factor">CONSTANT: roman-digits
{ "m" "cm" "d" "cd" "c" "xc" "l" "xl" "x" "ix" "v" "iv" "i" }

CONSTANT: roman-values
{ 1000 900 500 400 100 90 50 40 10 9 5 4 1 }

: roman> ( str -- n )
>lower [ roman-digit>= ] monotonic-split
[ roman-value ] map-sum ;

: roman-digit>= ( ch1 ch2 -- ? ) [ roman-digit-index ] bi@ >= ;

: roman-digit-index ( ch -- n ) 1string roman-digits index ;

: roman-value (seq -- n )
[ [ roman-digit-value ] map ] [ all-eq? ] bi
[ sum ] [ first2 swap - ] if ;

: roman-digit-value ( ch -- n )
roman-digit-index roman-values nth ;</syntaxhighlight>

=={{header|FALSE}}==
<syntaxhighlight lang="false">[ 32| {get value of Roman digit on stack}
$'m= $[\% 1000\]? ~[
$'d= $[\% 500\]? ~[
$'c= $[\% 100\]? ~[
$'l= $[\% 50\]? ~[
$'x= $[\% 10\]? ~[
$'v= $[\% 5\]? ~[
$'i= $[\% 1\]? ~[
% 0
]?]?]?]?]?]?]?
]r:

0 {accumulator}
^r;! {read first Roman digit}
[^r;!$][ {read another, and as long as it is valid...}
\$@@\$@@ {copy previous and current}
\>[\_\]? {if previous smaller than current, negate previous}
@@+\ {add previous to accumulator}
]#
%+. {add final digit to accumulator and output}
10, {and a newline}</syntaxhighlight>

{{out}}

<pre>$ ./false -q romandec.f <<<MCMXC
1990
$ ./false -q romandec.f <<<MMVIII
2008
$ ./false -q romandec.f <<<MDCLXVI
1666
$ ./false -q romandec.f <<<MMXXI
2021</pre>

=={{header|Forth}}==
<syntaxhighlight lang="forth">create (arabic)
1000 128 * char M + ,
500 128 * char D + ,
100 128 * char C + ,
50 128 * char L + ,
10 128 * char X + ,
5 128 * char V + ,
1 128 * char I + ,
does>
7 cells bounds do
i @ over over 127 and = if nip 7 rshift leave else drop then
1 cells +loop dup
;

: >arabic
0 dup >r >r
begin
over over
while
c@ dup (arabic) rot <>
while
r> over r> over over > if 2* negate + else drop then + swap >r >r 1 /string
repeat then drop 2drop r> r> drop
;

s" MCMLXXXIV" >arabic .</syntaxhighlight>


<syntaxhighlight lang="forth">\ decode roman numerals using Forth methodology
\ create words to describe and solve the problem
\ ANS/ISO Forth

\ state holders
VARIABLE OLDNDX
VARIABLE CURNDX
VARIABLE NEGFLAG

DECIMAL
CREATE VALUES ( -- addr) 0 , 1 , 5 , 10 , 50 , 100 , 500 , 1000 ,

: NUMERALS ( -- addr len) S" IVXLCDM" ; \ 1st char is a blank
: [] ( n addr -- addr') SWAP CELLS + ; \ array address calc.
: INIT ( -- ) CURNDX OFF OLDNDX OFF NEGFLAG OFF ;
: REMEMBER ( ndx -- ndx ) CURNDX @ OLDNDX ! DUP CURNDX ! ;
: ]VALUE@ ( ndx -- n ) REMEMBER VALUES [] @ ;
HEX
: TOUPPER ( char -- char ) 05F AND ;

DECIMAL
: >INDEX ( char -- ndx) TOUPPER >R NUMERALS TUCK R> SCAN NIP -
DUP 7 > ABORT" Invalid Roman numeral" ;

: >VALUE ( char -- n ) >INDEX ]VALUE@ ;
: ?ILLEGAL ( ndx -- ) CURNDX @ OLDNDX @ = NEGFLAG @ AND ABORT" Illegal format" ;

: ?NEGATE ( n -- +n | -n) \ conditional NEGATE
CURNDX @ OLDNDX @ <
IF NEGFLAG ON NEGATE
ELSE ?ILLEGAL NEGFLAG OFF
THEN ;

: >ARABIC ( addr len -- n )
INIT
0 -ROT \ accumulator under the stack string args
1- BOUNDS \ convert addr len to two addresses
SWAP DO \ index the string from back to front
I C@ >VALUE ?NEGATE +
-1 +LOOP ;
</syntaxhighlight>
Alternative Version Forth Console Test
<pre>\ test code ok
S" i" >ARABIC . 1 ok
S" ii" >ARABIC . 2 ok
S" iv" >ARABIC . 4 ok
S" mdclxvi" >ARABIC . 1666 ok
S" mm" >ARABIC . 2000 ok
S" mmm" >ARABIC . 3000 ok
S" MCMLIV" >ARABIC . 1954 ok
S" mcmxlvi" >ARABIC . 1946 ok
S" mdc" >ARABIC . 1600 ok
S" mdcl" >ARABIC . 1650 ok
S" mdclxvi" >ARABIC . 1666 ok
S" mcmlxxxiv" >ARABIC . 1984 ok
</PRE>


=={{header|Fortran}}==
=={{header|Fortran}}==
{{works with|Fortran|90 and later}}
{{works with|Fortran|90 and later}}
<lang fortran>program Roman_decode
<syntaxhighlight lang="fortran">program Roman_decode
implicit none
implicit none
Line 326: Line 3,331:
end do
end do
end function decode
end function decode
end program Roman_decode</lang>
end program Roman_decode</syntaxhighlight>
{{out}}
Output<pre> 1990 2008 1666</pre>
<pre> 1990 2008 1666</pre>

=={{header|Go}}==
=={{header|Go}}==
For fluff, the unicode overbar is recognized as a factor of 1000, [http://en.wikipedia.org/wiki/Roman_numerals#Large_numbers as described in WP].
For fluff, the unicode overbar is recognized as a factor of 1000, [http://en.wikipedia.org/wiki/Roman_numerals#Large_numbers as described in WP].
<lang go>package main
<syntaxhighlight lang="go">package main


import (
import (
"errors"
"fmt"
"fmt"
"os"
)
)


var m = map[int]int{
var m = map[rune]int{
'I': 1,
'I': 1,
'V': 5,
'V': 5,
Line 347: Line 3,354:
}
}


func parseRoman(s string) (r int, err os.Error) {
func parseRoman(s string) (r int, err error) {
if s == "" {
if s == "" {
return 0, os.NewError("Empty string")
return 0, errors.New("Empty string")
}
}
is := []int(s) // easier to convert string up front
is := []rune(s) // easier to convert string up front
var c0 int // c0: roman character last read
var c0 rune // c0: roman character last read
var cv0 int // cv0: value of cv
var cv0 int // cv0: value of cv


// the key to the algorithm is to process digits from right to left
// the key to the algorithm is to process digits from right to left
Line 359: Line 3,366:
// read roman digit
// read roman digit
c := is[i]
c := is[i]
k := c == 0x305 // unicode overbar combining character
k := c == '\u0305' // unicode overbar combining character
if k {
if k {
if i == 0 {
if i == 0 {
return 0, os.NewError("Overbar combining character invalid" +
return 0, errors.New(
" at position 0")
"Overbar combining character invalid at position 0")
}
}
i--
i--
Line 371: Line 3,378:
if cv == 0 {
if cv == 0 {
if c == 0x0305 {
if c == 0x0305 {
return 0, os.NewError(fmt.Sprintf(
return 0, fmt.Errorf(
"Overbar combining character invalid at position %d", i))
"Overbar combining character invalid at position %d", i)
} else {
} else {
return 0, os.NewError(fmt.Sprintf(
return 0, fmt.Errorf(
"Character unrecognized as Roman digit: %c", c))
"Character unrecognized as Roman digit: %c", c)
}
}
}
}
Line 385: Line 3,392:
// handle cases of new, same, subtractive, changed, in that order.
// handle cases of new, same, subtractive, changed, in that order.
switch {
switch {
default: // case 4: digit change
default: // case 4: digit change
fallthrough
fallthrough
case c0 == 0: // case 1: no previous digit
case c0 == 0: // case 1: no previous digit
c0 = c
c0 = c
cv0 = cv
cv0 = cv
case c == c0: // case 2: same digit
case c == c0: // case 2: same digit
case cv*5 == cv0 || cv*10 == cv0: // case 3: subtractive
case cv*5 == cv0 || cv*10 == cv0: // case 3: subtractive
// handle next digit as new.
// handle next digit as new.
Line 413: Line 3,420:
}
}
}
}
}</lang>
}</syntaxhighlight>
{{out}}
Output:
<pre>
<pre>
MCMXC == 1990
MCMXC == 1990
Line 420: Line 3,427:
MDCLXVI == 1666
MDCLXVI == 1666
</pre>
</pre>

Simpler:
<syntaxhighlight lang="go">package main

import (
"fmt"
)

var m = map[rune]int{
'I': 1,
'V': 5,
'X': 10,
'L': 50,
'C': 100,
'D': 500,
'M': 1000,
}

// function, per task description
func from_roman(roman string) (arabic int) {
last_digit := 1000
for _, r := range roman {
digit := m[r]
if last_digit < digit {
arabic -= 2 * last_digit
}
last_digit = digit
arabic += digit
}

return arabic
}

func main() {
// parse three numbers mentioned in task description
for _, roman_digit := range []string{"MCMXC", "MMVIII", "MDCLXVI"} {
fmt.Printf("%-10s == %d\n", roman_digit, from_roman(roman_digit))
}
}</syntaxhighlight>

=={{header|Golo}}==
<syntaxhighlight lang="golo">#!/usr/bin/env golosh
----
This module converts a Roman numeral into a decimal number.
----
module Romannumeralsdecode

augment java.lang.Character {

function decode = |this| -> match {
when this == 'I' then 1
when this == 'V' then 5
when this == 'X' then 10
when this == 'L' then 50
when this == 'C' then 100
when this == 'D' then 500
when this == 'M' then 1000
otherwise 0
}
}

augment java.lang.String {

function decode = |this| {
var accumulator = 0
foreach i in [0..this: length()] {
let currentChar = this: charAt(i)
let nextChar = match {
when i + 1 < this: length() then this: charAt(i + 1)
otherwise null
}
if (currentChar: decode() < (nextChar?: decode() orIfNull 0)) {
# if this is something like IV or IX or whatever
accumulator = accumulator - currentChar: decode()
} else {
accumulator = accumulator + currentChar: decode()
}
}
return accumulator
}
}

function main = |args| {
println("MCMXC = " + "MCMXC": decode())
println("MMVIII = " + "MMVIII": decode())
println("MDCLXVI = " + "MDCLXVI": decode())
}
</syntaxhighlight>

=={{header|Groovy}}==
Solution:
<syntaxhighlight lang="groovy">enum RomanDigits {
I(1), V(5), X(10), L(50), C(100), D(500), M(1000);
private magnitude;
private RomanDigits(magnitude) { this.magnitude = magnitude }
String toString() { super.toString() + "=${magnitude}" }
static BigInteger parse(String numeral) {
assert numeral != null && !numeral.empty
def digits = (numeral as List).collect {
RomanDigits.valueOf(it)
}
def L = digits.size()
(0..<L).inject(0g) { total, i ->
def sign = (i == L - 1 || digits[i] >= digits[i+1]) ? 1 : -1
total + sign * digits[i].magnitude
}
}
}</syntaxhighlight>
Test:
<syntaxhighlight lang="groovy">println """
Digit Values = ${RomanDigits.values()}
M => ${RomanDigits.parse('M')}
MCXI => ${RomanDigits.parse('MCXI')}
CMXI => ${RomanDigits.parse('CMXI')}
MCM => ${RomanDigits.parse('MCM')}
MCMXC => ${RomanDigits.parse('MCMXC')}
MMVIII => ${RomanDigits.parse('MMVIII')}
MMIX => ${RomanDigits.parse('MMIX')}
MCDXLIV => ${RomanDigits.parse('MCDXLIV')}
MDCLXVI => ${RomanDigits.parse('MDCLXVI')}
"""</syntaxhighlight>
{{out}}
<pre>Digit Values = [I=1, V=5, X=10, L=50, C=100, D=500, M=1000]
M => 1000
MCXI => 1111
CMXI => 911
MCM => 1900
MCMXC => 1990
MMVIII => 2008
MMIX => 2009
MCDXLIV => 1444
MDCLXVI => 1666</pre>


=={{header|Haskell}}==
=={{header|Haskell}}==


====Simple declarative idiom====
<lang Haskell>import Data.List (isPrefixOf)

Compiles with GHC.

<syntaxhighlight lang="haskell">
module Main where

------------------------
-- DECODER FUNCTION --
------------------------

decodeDigit :: Char -> Int
decodeDigit 'I' = 1
decodeDigit 'V' = 5
decodeDigit 'X' = 10
decodeDigit 'L' = 50
decodeDigit 'C' = 100
decodeDigit 'D' = 500
decodeDigit 'M' = 1000
decodeDigit _ = error "invalid digit"

-- We process a Roman numeral from right to left, digit by digit, adding the value.
-- If a digit is lower than the previous then its value is negative.
-- The first digit is always positive.

decode roman = decodeRoman startValue startValue rest
where
(first:rest) = reverse roman
startValue = decodeDigit first

decodeRoman :: Int -> Int -> [Char] -> Int
decodeRoman lastSum _ [] = lastSum
decodeRoman lastSum lastValue (digit:rest) = decodeRoman updatedSum digitValue rest
where
digitValue = decodeDigit digit
updatedSum = (if digitValue < lastValue then (-) else (+)) lastSum digitValue

------------------
-- TEST SUITE --
------------------

main = do
test "MCMXC" 1990
test "MMVIII" 2008
test "MDCLXVI" 1666

test roman expected = putStrLn (roman ++ " = " ++ (show (arabic)) ++ remark)
where
arabic = decode roman
remark = " (" ++ (if arabic == expected then "PASS" else ("FAIL, expected " ++ (show expected))) ++ ")"
</syntaxhighlight>

{{Out}}
<pre>
MCMXC = 1990 (PASS)
MMVIII = 2008 (PASS)
MDCLXVI = 1666 (PASS)
</pre>

====Same logic as above but in a functional idiom====

<syntaxhighlight lang="haskell">
module Main where

------------------------
-- DECODER FUNCTION --
------------------------

decodeDigit :: Char -> Int
decodeDigit 'I' = 1
decodeDigit 'V' = 5
decodeDigit 'X' = 10
decodeDigit 'L' = 50
decodeDigit 'C' = 100
decodeDigit 'D' = 500
decodeDigit 'M' = 1000
decodeDigit _ = error "invalid digit"

-- We process a Roman numeral from right to left, digit by digit, adding the value.
-- If a digit is lower than the previous then its value is negative.
-- The first digit is always positive.

decode roman = fst (foldl addValue (0, 0) (reverse roman))
where
addValue (lastSum, lastValue) digit = (updatedSum, value)
where
value = decodeDigit digit;
updatedSum = (if value < lastValue then (-) else (+)) lastSum value

------------------
-- TEST SUITE --
------------------

main = do
test "MCMXC" 1990
test "MMVIII" 2008
test "MDCLXVI" 1666

test roman expected = putStrLn (roman ++ " = " ++ (show (arabic)) ++ remark)
where
arabic = decode roman
remark = " (" ++ (if arabic == expected then "PASS" else ("FAIL, expected " ++ (show expected))) ++ ")"
</syntaxhighlight>

====List comprehension====
<syntaxhighlight lang="haskell">import Data.List (isPrefixOf)


mapping = [("M",1000),("CM",900),("D",500),("CD",400),("C",100),("XC",90),
mapping = [("M",1000),("CM",900),("D",500),("CD",400),("C",100),("XC",90),
Line 431: Line 3,678:
toArabic "" = 0
toArabic "" = 0
toArabic str = num + toArabic xs
toArabic str = num + toArabic xs
where (num, xs):_ = [ (num, drop (length n) str) | (n,num) <- mapping, isPrefixOf n str ]</lang>
where (num, xs):_ = [ (num, drop (length n) str) | (n,num) <- mapping, isPrefixOf n str ]</syntaxhighlight>
Usage:
Usage:
<pre>
<pre>
Line 439: Line 3,686:
2008
2008
ghci> toArabic "MDCLXVI"
ghci> toArabic "MDCLXVI"
1666
1666</pre>

</pre>
====mapAccum====
Or, expressing '''romanValue''' in terms of '''mapAccumL''' (avoiding recursive descent, and visiting each k v pair just once)
<syntaxhighlight lang="haskell">import Data.Bifunctor (bimap)
import Data.List (isPrefixOf, mapAccumL)

romanValue :: String -> Int
romanValue =
let tr s (k, v) =
until
(not . isPrefixOf k . fst)
(bimap ((drop . length) k) (v +))
(s, 0)
in sum
. snd
. flip
(mapAccumL tr)
[ ("M", 1000),
("CM", 900),
("D", 500),
("CD", 400),
("C", 100),
("XC", 90),
("L", 50),
("XL", 40),
("X", 10),
("IX", 9),
("V", 5),
("IV", 4),
("I", 1)
]

main :: IO ()
main =
mapM_
(print . romanValue)
[ "MDCLXVI",
"MCMXC",
"MMVIII",
"MMXVI",
"MMXVII"
]</syntaxhighlight>

Or, in a '''mapAccumR''' version:
<syntaxhighlight lang="haskell">import Data.List (mapAccumR)
import qualified Data.Map.Strict as M
import Data.Maybe (maybe)

fromRoman :: String -> Maybe Int
fromRoman cs =
let go l r
| l > r = (- r, l)
| otherwise = (r, l)
in traverse (`M.lookup` mapRoman) cs
>>= ( Just . sum . ((:) <$> fst <*> snd)
. mapAccumR go 0
)

mapRoman :: M.Map Char Int
mapRoman =
M.fromList $
zip
"MDCLXVI "
[ 1000,
500,
100,
50,
10,
5,
1,
0
]

--------------------------- TEST -------------------------
main :: IO ()
main =
putStrLn $
fTable
"Decoding Roman numbers:\n"
show
(maybe "Unrecognised character" show)
fromRoman
[ "MDCLXVI",
"MCMXC",
"MMVIII",
"MMXVI",
"MMXVIII",
"MMXBIII"
]

------------------------ FORMATTING ----------------------
fTable ::
String ->
(a -> String) ->
(b -> String) ->
(a -> b) ->
[a] ->
String
fTable s xShow fxShow f xs =
unlines $
s :
fmap
( ((<>) . rjust w ' ' . xShow)
<*> ((" -> " <>) . fxShow . f)
)
xs
where
rjust n c = drop . length <*> (replicate n c <>)
w = maximum (length . xShow <$> xs)</syntaxhighlight>
{{Out}}
<pre>Decoding Roman numbers:

"MDCLXVI" -> 1666
"MCMXC" -> 1990
"MMVIII" -> 2008
"MMXVI" -> 2016
"MMXVIII" -> 2018
"MMXBIII" -> Unrecognised character</pre>

====Fold====
An alternative solution using a fold. (This turns out to be the fastest of the four approaches here) {{Trans|F#}}

<syntaxhighlight lang="haskell">import qualified Data.Map.Strict as M

fromRoman :: String -> Int
fromRoman xs = partialSum + lastDigit
where
(partialSum, lastDigit) = foldl accumulate (0, 0) (evalRomanDigit <$> xs)
accumulate (partial, lastDigit) newDigit
| newDigit <= lastDigit = (partial + lastDigit, newDigit)
| otherwise = (partial - lastDigit, newDigit)

mapRoman :: M.Map Char Int
mapRoman =
M.fromList
[ ('I', 1)
, ('V', 5)
, ('X', 10)
, ('L', 50)
, ('C', 100)
, ('D', 500)
, ('M', 1000)
]

evalRomanDigit :: Char -> Int
evalRomanDigit c =
let mInt = M.lookup c mapRoman
in case mInt of
Just x -> x
_ -> error $ c : " is not a roman digit"

main :: IO ()
main = print $ fromRoman <$> ["MDCLXVI", "MCMXC", "MMVIII", "MMXVI", "MMXVII"]</syntaxhighlight>


Where the left fold above could also be rewritten [http://wiki.haskell.org/Foldr_Foldl_Foldl%27 | as a right fold].
<syntaxhighlight lang="haskell">import qualified Data.Map.Strict as M
import Data.Maybe (maybe)

------------------ ROMAN NUMERALS DECODED ----------------

mapRoman :: M.Map Char Int
mapRoman =
M.fromList $
zip "IVXLCDM" $
scanl (*) 1 (cycle [5, 2])

fromRoman :: String -> Maybe Int
fromRoman cs =
let op l r
| l >= r = (+)
| otherwise = (-)
in snd
. foldr
(\l (r, n) -> (l, op l r n l))
(0, 0)
<$> traverse (`M.lookup` mapRoman) cs

--------------------------- TEST -------------------------
main :: IO ()
main =
putStrLn $
fTable
"Roman numeral decoding as a right fold:\n"
show
(maybe "(Unrecognised character seen)" show)
fromRoman
[ "MDCLXVI",
"MCMXC",
"MMVIII",
"MMXVI",
"MMXVII",
"QQXVII"
]

------------------------ FORMATTING ----------------------

fTable ::
String ->
(a -> String) ->
(b -> String) ->
(a -> b) ->
[a] ->
String
fTable s xShow fxShow f xs =
unlines $
s :
fmap
( ((<>) . rjust w ' ' . xShow)
<*> ((" -> " <>) . fxShow . f)
)
xs
where
rjust n c = drop . length <*> (replicate n c <>)
w = maximum (length . xShow <$> xs)</syntaxhighlight>
{{Out}}
<pre>Roman numeral decoding as a right fold:

"MDCLXVI" -> 1666
"MCMXC" -> 1990
"MMVIII" -> 2008
"MMXVI" -> 2016
"MMXVII" -> 2017
"QQXVII" -> (Unrecognised character seen)</pre>

====sum . catMaybes====

Summing a list of Map.lookup results on indexed [Char, Char] pairs.

(Probably more trouble than it's worth in practice, but at least an illustration of some Data.Maybe and Data.Map functions)

<syntaxhighlight lang="haskell">import qualified Data.Map.Strict as M (Map, fromList, lookup)
import Data.Maybe (isNothing, isJust, fromJust, catMaybes)
import Data.List (mapAccumL)
mapRoman :: M.Map String Int
mapRoman =
M.fromList
[ ("M", 1000)
, ("CM", 900)
, ("D", 500)
, ("CD", 400)
, ("C", 100)
, ("XC", 90)
, ("L", 50)
, ("XL", 40)
, ("X", 10)
, ("IX", 9)
, ("V", 5)
, ("IV", 4)
, ("I", 1)
]
fromRoman :: String -> Int
fromRoman s =
let value k = M.lookup k mapRoman
in sum . catMaybes . snd $
mapAccumL
(\mi (l, r, i) ->
let mValue = value [l, r] -- mapRoman lookup of [left, right] Chars
(lastPair, pairValue)
| isJust mValue = (Just i, mValue) -- Pair match: index updated
| isNothing mi || i - fromJust mi > 1 = (mi, value [l])
| otherwise = (mi, Nothing) -- Left Char was counted in pair
in (lastPair, pairValue))
Nothing -- Accumulator – maybe Index to last matched Char pair
(zip3 s (tail s ++ " ") [0 ..]) -- Indexed character pairs
main :: IO ()
main = print $ fromRoman <$> ["MDCLXVI", "MCMXC", "MMVIII", "MMXVI", "MMXVII"]</syntaxhighlight>
{{Out}}
<pre>[1666,1990,2008,2016,2017]</pre>

=={{header|Hoon}}==

Library file (e.g. <code>/lib/rhonda.hoon</code>):

<syntaxhighlight lang="hoon">|%
++ parse
|= t=tape ^- @ud
=. t (cass t)
=| result=@ud
|-
?~ t result
?~ t.t (add result (from-numeral i.t))
=+ [a=(from-numeral i.t) b=(from-numeral i.t.t)]
?: (gte a b) $(result (add result a), t t.t)
$(result (sub (add result b) a), t t.t.t)
++ yield
|= n=@ud ^- tape
=| result=tape
=/ values to-numeral
|-
?~ values result
?: (gte n -.i.values)
$(result (weld result +.i.values), n (sub n -.i.values))
$(values t.values)
++ from-numeral
|= c=@t ^- @ud
?: =(c 'i') 1
?: =(c 'v') 5
?: =(c 'x') 10
?: =(c 'l') 50
?: =(c 'c') 100
?: =(c 'd') 500
?: =(c 'm') 1.000
!!
++ to-numeral
^- (list [@ud tape])
:*
[1.000 "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"]
~
==
--</syntaxhighlight>

Script file ("generator") (e.g. <code>/gen/roman.hoon</code>):

<syntaxhighlight lang="hoon">/+ *roman
:- %say
|= [* [x=$%([%from-roman tape] [%to-roman @ud]) ~] ~]
:- %noun
^- tape
?- -.x
%from-roman "{<(parse +.x)>}"
%to-roman (yield +.x)
==</syntaxhighlight>


=={{header|Icon}} and {{header|Unicon}}==
=={{header|Icon}} and {{header|Unicon}}==
<lang Icon>link numbers
<syntaxhighlight lang="icon">link numbers


procedure main()
procedure main()
every R := "MCMXC"|"MDCLXVI"|"MMVIII" do
every R := "MCMXC"|"MDCLXVI"|"MMVIII" do
write(R, " = ",unroman(R))
write(R, " = ",unroman(R))
end</lang>
end</syntaxhighlight>

{{libheader|Icon Programming Library}}
{{libheader|Icon Programming Library}}
[http://www.cs.arizona.edu/icon/library/src/procs/numbers.icn numbers.icn provides unroman]
[http://www.cs.arizona.edu/icon/library/src/procs/numbers.icn numbers.icn provides unroman]


The code for this procedure is copied below:<lang Icon>procedure unroman(s) #: convert Roman numeral to integer
The code for this procedure is copied below:
<syntaxhighlight lang="icon">procedure unroman(s) #: convert Roman numeral to integer
local nbr,lastVal,val
local nbr,lastVal,val


Line 472: Line 4,056:
}
}
return nbr
return nbr
end</lang>
end</syntaxhighlight>
{{out}}

Output:<pre>MCMXC = 1990
<pre>MCMXC = 1990
MDCLXVI = 1666
MDCLXVI = 1666
MMVIII = 2008</pre>
MMVIII = 2008</pre>


=={{header|J}}==
=={{header|Insitux}}==


{{Trans|Clojure}}
<lang j>rom2d=: [: (+/ .* _1^ 0,~ 2</\ ]) 1 5 10 50 100 500 1000 {~ 'IVXLCDM'&i.</lang>


<syntaxhighlight lang="insitux">
Example use:
(var numerals {"M" 1000 "D" 500 "C" 100 "L" 50 "X" 10 "V" 5 "I" 1})


; Approach A
<lang j> rom2d 'MCMXC'
(function ro->ar r
(-> (reverse (upper-case r))
(map numerals)
(split-with val)
(map (.. +0))
(reduce @(((< % %1) + -)))))

; Approach B
(function ro->ar r
(-> (upper-case r)
(map numerals)
@(reduce (fn [sum lastv] curr [(+ sum curr ((< lastv curr) (* -2 lastv) 0)) curr]) [0 0])
0))

(map ro->ar ["MDCLXVI" "MMMCMXCIX" "XLVIII" "MMVIII"])
</syntaxhighlight>

{{out}}

<pre>
[1666 3999 48 2008]
</pre>

=={{header|J}}==
<syntaxhighlight lang="j">rom2d=: [: (+/ .* _1^ 0,~ 2</\ ]) 1 5 10 50 100 500 1000 {~ 'IVXLCDM'&i.</syntaxhighlight>
Example use:
<syntaxhighlight lang="j"> rom2d 'MCMXC'
1990
1990
rom2d 'MDCLXVI'
rom2d 'MDCLXVI'
1666
1666
rom2d 'MMVIII'
rom2d 'MMVIII'
2008</lang>
2008</syntaxhighlight>

=={{header|Java}}==
=={{header|Java}}==
{{works with|Java|1.5+}}
<lang java>public class Roman{
<syntaxhighlight lang="java5">public class Roman {
private static int decodeSingle(char letter){
private static int decodeSingle(char letter) {
switch(letter){
switch(letter) {
case 'M': return 1000;
case 'M': return 1000;
case 'D': return 500;
case 'D': return 500;
Line 504: Line 4,118:
}
}
}
}
public static int decode(String roman){
public static int decode(String roman) {
int result = 0;
int result = 0;
String uRoman = roman.toUpperCase(); //case-insensitive
String uRoman = roman.toUpperCase(); //case-insensitive
for(int i = 0;i < uRoman.length() - 1;i++){//loop over all but the last character
for(int i = 0;i < uRoman.length() - 1;i++) {//loop over all but the last character
//if this character has a lower value than the next character
//if this character has a lower value than the next character
if(decodeSingle(uRoman.charAt(i)) < decodeSingle(uRoman.charAt(i + 1))){
if (decodeSingle(uRoman.charAt(i)) < decodeSingle(uRoman.charAt(i+1))) {
//subtract it
//subtract it
result -= decodeSingle(uRoman.charAt(i));
result -= decodeSingle(uRoman.charAt(i));
}else{
} else {
//add it
//add it
result += decodeSingle(uRoman.charAt(i));
result += decodeSingle(uRoman.charAt(i));
Line 522: Line 4,136:
}
}
public static void main(String[] args){
public static void main(String[] args) {
System.out.println(decode("MCMXC")); //1990
System.out.println(decode("MCMXC")); //1990
System.out.println(decode("MMVIII")); //2008
System.out.println(decode("MMVIII")); //2008
System.out.println(decode("MDCLXVI")); //1666
System.out.println(decode("MDCLXVI")); //1666
}
}
}</lang>
}</syntaxhighlight>
{{out}}
Output:
<pre>1990
<pre>1990
2008
2008
1666</pre>
1666</pre>
{{works with|Java|1.8+}}
<syntaxhighlight lang="java5">import java.util.Set;
import java.util.EnumSet;
import java.util.Collections;
import java.util.stream.Collectors;
import java.util.stream.LongStream;

public interface RomanNumerals {
public enum Numeral {
M(1000), CM(900), D(500), CD(400), C(100), XC(90), L(50), XL(40), X(10), IX(9), V(5), IV(4), I(1);

public final long weight;

private static final Set<Numeral> SET = Collections.unmodifiableSet(EnumSet.allOf(Numeral.class));

private Numeral(long weight) {
this.weight = weight;
}

public static Numeral getLargest(long weight) {
return SET.stream()
.filter(numeral -> weight >= numeral.weight)
.findFirst()
.orElse(I)
;
}
};

public static String encode(long n) {
return LongStream.iterate(n, l -> l - Numeral.getLargest(l).weight)
.limit(Numeral.values().length)
.filter(l -> l > 0)
.mapToObj(Numeral::getLargest)
.map(String::valueOf)
.collect(Collectors.joining())
;
}

public static long decode(String roman) {
long result = new StringBuilder(roman.toUpperCase()).reverse().chars()
.mapToObj(c -> Character.toString((char) c))
.map(numeral -> Enum.valueOf(Numeral.class, numeral))
.mapToLong(numeral -> numeral.weight)
.reduce(0, (a, b) -> a + (a <= b ? b : -b))
;
if (roman.charAt(0) == roman.charAt(1)) {
result += 2 * Enum.valueOf(Numeral.class, roman.substring(0, 1)).weight;
}
return result;
}

public static void test(long n) {
System.out.println(n + " = " + encode(n));
System.out.println(encode(n) + " = " + decode(encode(n)));
}

public static void main(String[] args) {
LongStream.of(1999, 25, 944).forEach(RomanNumerals::test);
}
}</syntaxhighlight>
{{out}}
<pre>1999 = MCMXCIX
MCMXCIX = 1999
25 = XXV
XXV = 25
944 = CMXLIV
CMXLIV = 944</pre>

=={{header|JavaScript}}==
===ES5===
====Imperative====
{{works with|Rhino}}
{{works with|SpiderMonkey}}
<syntaxhighlight lang="javascript">var Roman = {
Values: [['CM', 900], ['CD', 400], ['XC', 90], ['XL', 40], ['IV', 4],
['IX', 9], ['V', 5], ['X', 10], ['L', 50],
['C', 100], ['M', 1000], ['I', 1], ['D', 500]],
UnmappedStr : 'Q',
parse: function(str) {
var result = 0
for (var i=0; i<Roman.Values.length; ++i) {
var pair = Roman.Values[i]
var key = pair[0]
var value = pair[1]
var regex = RegExp(key)
while (str.match(regex)) {
result += value
str = str.replace(regex, Roman.UnmappedStr)
}
}
return result
}
}

var test_data = ['MCMXC', 'MDCLXVI', 'MMVIII']
for (var i=0; i<test_data.length; ++i) {
var test_datum = test_data[i]
print(test_datum + ": " + Roman.parse(test_datum))
}</syntaxhighlight>
{{out}}
<pre>MCMXC: 1990
MDCLXVI: 1666
MMVIII: 2008
</pre>
====Functional====
{{Trans|Haskell}}
(isPrefixOf example)
<syntaxhighlight lang="javascript">(function (lstTest) {
var mapping = [["M", 1000], ["CM", 900], ["D", 500], ["CD", 400], ["C", 100], [
"XC", 90], ["L", 50], ["XL", 40], ["X", 10], ["IX", 9], ["V", 5], ["IV",
4], ["I", 1]];
// s -> n
function romanValue(s) {
// recursion over list of characters
// [c] -> n
function toArabic(lst) {
return lst.length ? function (xs) {
var lstParse = chain(mapping, function (lstPair) {
return isPrefixOf(
lstPair[0], xs
) ? [lstPair[1], drop(lstPair[0].length, xs)] : []
});
return lstParse[0] + toArabic(lstParse[1]);
}(lst) : 0
}
return toArabic(s.split(''));
}
// Monadic bind (chain) for lists
function chain(xs, f) {
return [].concat.apply([], xs.map(f));
}
// [a] -> [a] -> Bool
function isPrefixOf(lstFirst, lstSecond) {
return lstFirst.length ? (
lstSecond.length ?
lstFirst[0] === lstSecond[0] && isPrefixOf(
lstFirst.slice(1), lstSecond.slice(1)
) : false
) : true;
}
// Int -> [a] -> [a]
function drop(n, lst) {
return n <= 0 ? lst : (
lst.length ? drop(n - 1, lst.slice(1)) : []
);
}
return lstTest.map(romanValue);
})(['MCMXC', 'MDCLXVI', 'MMVIII']);</syntaxhighlight>
{{Out}}
<syntaxhighlight lang="javascript">[1990, 1666, 2008]</syntaxhighlight>

or, more natively:
<syntaxhighlight lang="javascript">(function (lstTest) {
function romanValue(s) {
return s.length ? function () {
var parse = [].concat.apply([], glyphs.map(function (g) {
return 0 === s.indexOf(g) ? [trans[g], s.substr(g.length)] : [];
}));
return parse[0] + romanValue(parse[1]);
}() : 0;
}
var trans = {
M: 1E3,
CM: 900,
D: 500,
CD: 400,
C: 100,
XC: 90,
L: 50,
XL: 40,
X: 10,
IX: 9,
V: 5,
IV: 4,
I: 1
},
glyphs = Object.keys(trans);
return lstTest.map(romanValue);
})(["MCMXC", "MDCLXVI", "MMVIII", "MMMM"]);</syntaxhighlight>
{{Out}}
<syntaxhighlight lang="javascript">[1990, 1666, 2008]</syntaxhighlight>

===ES6===
====Recursion====
<syntaxhighlight lang="javascript">(() => {
// romanValue :: String -> Int
const romanValue = s =>
s.length ? (() => {
const parse = [].concat(
...glyphs.map(g => 0 === s.indexOf(g) ? (
[dctTrans[g], s.substr(g.length)]
) : [])
);
return parse[0] + romanValue(parse[1]);
})() : 0;

// dctTrans :: {romanKey: Integer}
const dctTrans = {
M: 1E3,
CM: 900,
D: 500,
CD: 400,
C: 100,
XC: 90,
L: 50,
XL: 40,
X: 10,
IX: 9,
V: 5,
IV: 4,
I: 1
};

// glyphs :: [romanKey]
const glyphs = Object.keys(dctTrans);

// TEST -------------------------------------------------------------------
return ["MCMXC", "MDCLXVI", "MMVIII", "MMMM"].map(romanValue);
})();</syntaxhighlight>
{{Out}}
<syntaxhighlight lang="javascript">[1990,1666,2008,4000]</syntaxhighlight>


====Folding from the right====
{{Trans|Haskell}}
(fold and foldr examples)
<syntaxhighlight lang="javascript">(() => {

// -------------- ROMAN NUMERALS DECODED ---------------

// Folding from right to left,
// lower leftward characters are subtracted,
// others are added.

// fromRoman :: String -> Int
const fromRoman = s =>
foldr(l => ([r, n]) => [
l,
l >= r ? (
n + l
) : n - l
])([0, 0])(
[...s].map(charVal)
)[1];

// charVal :: Char -> Maybe Int
const charVal = k => {
const v = {
I: 1,
V: 5,
X: 10,
L: 50,
C: 100,
D: 500,
M: 1000
} [k];
return v !== undefined ? v : 0;
};

// ----------------------- TEST ------------------------
const main = () => [
'MDCLXVI', 'MCMXC', 'MMVIII', 'MMXVI', 'MMXVII'
]
.map(fromRoman)
.join('\n');


// ----------------- GENERIC FUNCTIONS -----------------

// foldr :: (a -> b -> b) -> b -> [a] -> b
const foldr = f =>
// Note that that the Haskell signature of foldr
// differs from that of foldl - the positions of
// accumulator and current value are reversed.
a => xs => [...xs].reduceRight(
(a, x) => f(x)(a),
a
);

// MAIN ---
return main();
})();</syntaxhighlight>
{{Out}}
<pre>1666
1990
2008
2016
2017</pre>

====Declarative====
<syntaxhighlight lang="javascript">
(() => {
function toNumeric(value) {
return value
.replace(/IV/, 'I'.repeat(4))
.replace(/V/g, 'I'.repeat(5))
.replace(/IX/, 'I'.repeat(9))
.replace(/XC/, 'I'.repeat(90))
.replace(/XL/, 'I'.repeat(40))
.replace(/X/g, 'I'.repeat(10))
.replace(/L/, 'I'.repeat(50))
.replace(/CD/, 'I'.repeat(400))
.replace(/CM/, 'I'.repeat(900))
.replace(/C/g, 'I'.repeat(100))
.replace(/D/g, 'I'.repeat(500))
.replace(/M/g, 'I'.repeat(1000))
.length;
}

const numerics = ["MMXVI", "MCMXC", "MMVIII", "MM", "MDCLXVI"]
.map(toNumeric);

console.log(numerics);
})();
</syntaxhighlight>

{{Out}}
<pre>
[2016, 1990, 2008, 2000, 1666]
</pre>

=={{header|jq}}==
{{works with|jq|1.4}}
This version requires the Roman numerals to be presented in upper case.
<syntaxhighlight lang="jq">def fromRoman:
def addRoman(n):
if length == 0 then n
elif startswith("M") then .[1:] | addRoman(1000 + n)
elif startswith("CM") then .[2:] | addRoman(900 + n)
elif startswith("D") then .[1:] | addRoman(500 + n)
elif startswith("CD") then .[2:] | addRoman(400 + n)
elif startswith("C") then .[1:] | addRoman(100 + n)
elif startswith("XC") then .[2:] | addRoman(90 + n)
elif startswith("L") then .[1:] | addRoman(50 + n)
elif startswith("XL") then .[2:] | addRoman(40 + n)
elif startswith("X") then .[1:] | addRoman(10 + n)
elif startswith("IX") then .[2:] | addRoman(9 + n)
elif startswith("V") then .[1:] | addRoman(5 + n)
elif startswith("IV") then .[2:] | addRoman(4 + n)
elif startswith("I") then .[1:] | addRoman(1 + n)
else
error("invalid Roman numeral: " + tostring)
end;
addRoman(0);</syntaxhighlight>
'''Example:'''
<syntaxhighlight lang="jq">[ "MCMXC", "MMVIII", "MDCLXVI" ] | map("\(.) => \(fromRoman)") | .[]</syntaxhighlight>
{{out}}
<syntaxhighlight lang="sh">$ jq -n -f -r fromRoman.jq
MCMXC => 1990
MMVIII => 2008
MDCLXVI => 1666</syntaxhighlight>

=={{header|Jsish}}==
Duplicate of the Jsish module used in [[Roman_numerals/Encode#Jsish]].

{{out}}
<pre>prompt$ jsish -e 'require("Roman"); puts(Roman.fromRoman("MDCLXVI"));'
1666</pre>

=={{header|Julia}}==
{{works with|Julia|0.6}}
'''The Function''':
<syntaxhighlight lang="julia">function parseroman(rnum::AbstractString)
romandigits = Dict('I' => 1, 'V' => 5, 'X' => 10, 'L' => 50,
'C' => 100, 'D' => 500, 'M' => 1000)
mval = accm = 0
for d in reverse(uppercase(rnum))
val = try
romandigits[d]
catch
throw(DomainError())
end
if val > mval maxval = val end
if val < mval
accm -= val
else
accm += val
end
end
return accm
end</syntaxhighlight>

This function is rather permissive. There are no limitations on the numbers of Roman numerals nor on their order. Because of this and because any out of order numerals subtract from the total represented, it is possible to represent zero and negative integers. Also mixed case representations are allowed. The function does throw an error if the string contains any invalid characters.

'''Test the code''':
<syntaxhighlight lang="julia">using Printf

test = ["I", "III", "IX", "IVI", "IIM",
"CMMDXL", "icv", "cDxLiV", "MCMLD", "ccccccd",
"iiiiiv", "MMXV", "MCMLXXXIV", "ivxmm", "SPQR"]
for rnum in test
@printf("%15s → %s\n", rnum, try parseroman(rnum) catch "not valid" end)
end</syntaxhighlight>

{{out}}
<pre> I → 1
III → 3
IX → 11
IVI → 7
IIM → 1002
CMMDXL → 2660
icv → 106
cDxLiV → 666
MCMLD → 2650
ccccccd → 1100
iiiiiv → 10
MMXV → 2015
MCMLXXXIV → 2186
ivxmm → 2016
SPQR → not valid</pre>


=={{header|K}}==
=={{header|K}}==
{{trans|J}}
{{trans|J}}
<lang k> romd: {v:1 5 10 50 100 500 1000@"IVXLCDM"?/:x; +/v*_-1^(>':v),0}</lang>
<syntaxhighlight lang="k"> romd: {v:1 5 10 50 100 500 1000@"IVXLCDM"?/:x; +/v*_-1^(>':v),0}</syntaxhighlight>

'''Example:'''
'''Example:'''
<syntaxhighlight lang="k"> romd'("MCMXC";"MMVIII";"MDCLXVI")
1990 2008 1666</syntaxhighlight>


=={{header|Kotlin}}==
<lang k> romd'("MCMXC";"MMVIII";"MDCLXVI")
As specified in the task description, there is no attempt to validate the form of the Roman number in the following program - invalid characters and ordering are simply ignored:
1990 2008 1666</lang>
<syntaxhighlight lang="scala">// version 1.0.6

fun romanDecode(roman: String): Int {
if (roman.isEmpty()) return 0
var n = 0
var last = 'O'
for (c in roman) {
when (c) {
'I' -> n += 1
'V' -> if (last == 'I') n += 3 else n += 5
'X' -> if (last == 'I') n += 8 else n += 10
'L' -> if (last == 'X') n += 30 else n += 50
'C' -> if (last == 'X') n += 80 else n += 100
'D' -> if (last == 'C') n += 300 else n += 500
'M' -> if (last == 'C') n += 800 else n += 1000
}
last = c
}
return n
}

fun main(args: Array<String>) {
val romans = arrayOf("I", "III", "IV", "VIII", "XLIX", "CCII", "CDXXXIII", "MCMXC", "MMVIII", "MDCLXVI")
for (roman in romans) println("${roman.padEnd(10)} = ${romanDecode(roman)}")
}</syntaxhighlight>

{{out}}
<pre>
I = 1
III = 3
IV = 4
VIII = 8
XLIX = 49
CCII = 202
CDXXXIII = 433
MCMXC = 1990
MMVIII = 2008
MDCLXVI = 1666
</pre>

=={{header|Lasso}}==
<syntaxhighlight lang="lasso">define br => '\r'
//decode roman
define decodeRoman(roman::string)::integer => {
local(ref = array('M'=1000, 'CM'=900, 'D'=500, 'CD'=400, 'C'=100, 'XC'=90, 'L'=50, 'XL'=40, 'X'=10, 'IX'=9, 'V'=5, 'IV'=4, 'I'=1))
local(out = integer)
while(#roman->size) => {
// need to use neset while instead of query expr to utilize loop_abort
while(loop_count <= #ref->size) => {
if(#roman->beginswith(#ref->get(loop_count)->first)) => {
#out += #ref->get(loop_count)->second
#roman->remove(1,#ref->get(loop_count)->first->size)
loop_abort
}
}
}
return #out
}

'MCMXC as integer is '+decodeRoman('MCMXC')
br
'MMVIII as integer is '+decodeRoman('MMVIII')
br
'MDCLXVI as integer is '+decodeRoman('MDCLXVI')</syntaxhighlight>

=={{header|LiveScript}}==

<syntaxhighlight lang="livescript">require! 'prelude-ls': {fold, sum}

# String → Number
decimal_of_roman = do
# [Number, Number] → String → [Number, Number]
_convert = ([acc, last_value], ch) ->
current_value = { M:1000 D:500 C:100 L:50 X:10 V:5 I:1 }[ch] ? 0
op = if last_value < current_value then (-) else (+)
[op(acc, last_value), current_value]
# fold the string and sum the resulting tuple (array)
fold(_convert, [0, 0]) >> sum

{[rom, decimal_of_roman rom] for rom in <[ MCMXC MMVII MDCLXVII MMMCLIX MCMLXXVII MMX ]>}</syntaxhighlight>

Output:
<pre>{"MCMXC":1990,"MMVII":2007,"MDCLXVII":1667,"MMMCLIX":3159,"MCMLXXVII":1977,"MMX":2010}</pre>

=={{header|Logo}}==
<syntaxhighlight lang="logo">; Roman numeral decoder

; First, some useful substring utilities
to starts_with? :string :prefix
if empty? :prefix [output "true]
if empty? :string [output "false]
if not equal? first :string first :prefix [output "false]
output starts_with? butfirst :string butfirst :prefix
end

to remove_prefix :string :prefix
if or empty? :prefix not starts_with? :string :prefix [output :string]
output remove_prefix butfirst :string butfirst :prefix
end

; Our list of Roman numeral values
make "values [[M 1000] [CM 900] [D 500] [CD 400] [C 100] [XC 90] [L 50]
[XL 40] [X 10] [IX 9] [V 5] [IV 4] [I 1]]

; Function to do the work
to from_roman :str
local "n make "n 0
foreach :values [
local "s make "s first ?
local "v make "v last ?
while [starts_with? :str :s] [
make "n sum n :v
make "str remove_prefix :str :s
]
]
output :n
end

foreach [MCMXC MDCLXVI MMVIII] [print (sentence (word ? "|: |) from_roman ?)]
bye</syntaxhighlight>
{{out}}
<pre>MCMXC: 1990
MDCLXVI: 1666
MMVIII: 2008
</pre>


=={{header|Lua}}==
=={{header|Lua}}==
<lang lua>function ToNumeral( roman )
<syntaxhighlight lang="lua">function ToNumeral( roman )
local Num = { ["M"] = 1000, ["D"] = 500, ["C"] = 100, ["L"] = 50, ["X"] = 10, ["V"] = 5, ["I"] = 1 }
local Num = { ["M"] = 1000, ["D"] = 500, ["C"] = 100, ["L"] = 50, ["X"] = 10, ["V"] = 5, ["I"] = 1 }
local numeral = 0
local numeral = 0
Line 564: Line 4,725:
return numeral
return numeral
end
end



print( ToNumeral( "MCMXC" ) )
print( ToNumeral( "MCMXC" ) )
print( ToNumeral( "MMVIII" ) )
print( ToNumeral( "MMVIII" ) )
print( ToNumeral( "MDCLXVI" ) )</lang>
print( ToNumeral( "MDCLXVI" ) )</syntaxhighlight>
<pre>1990
2008
1666</pre>

=={{header|M2000 Interpreter}}==
Maximum Roman number is MMMCMXCIX (3999)

<syntaxhighlight lang="m2000 interpreter">
Module RomanNumbers {
flush ' empty current stack
gosub Initialize
document Doc$
while not empty
read rom$
print rom$;"=";RomanEval$(rom$)
Doc$=rom$+"="+RomanEval$(rom$)+{
}
end while
Clipboard Doc$
end
Initialize:
function RomanEval$(rom$) {
Flush
="invalid"
if filter$(rom$,"MDCLXVI")<>"" Then Exit
\\ "Y" is in top of stack
Push "CM", "MD", "Q"
Push "CD", "MD","W"
Push "XC", "DL", "E"
Push "XL", "X","R"
Push "IX","V","T"
Push "IV","I","Y"
\\ stack flush to doublerom
doublerom=[]
\\ "M" is in top of stack
Data "M", 1000, "Q",900
Data "D", 500,"W", 400
Data "C",100,"E",90
Data "L",50,"R", 40
Data "X", 10, "T", 9
Data "V", 5, "Y", 4, "I",1
\\ stack flush to singlerom
singlerom=[]
acc=0
value=0
count=0
stack doublerom {
if empty then exit
read rep$,exclude$,cc$
i=instr(rom$,cc$)
if i >0 then
tmp$=mid$(rom$,i+2)
L=Len(tmp$)
if L>0 then if Len(filter$(tmp$, exclude$))<>L then rom$="A": exit
if Instr(rom$,mid$(rom$,i,1))<i then rom$="A": exit
insert i,2 rom$=rep$ ' replace at pos i with rep$ and place a space to i+1
end if
loop
}
rom$=filter$(rom$," ") ' remove spaces if exist

stack singlerom {
if empty then exit
read cc$, value
count=0
while left$(rom$,1)=cc$
insert 1, 1 rom$=""
count++
acc+=value
end while
if count>3 then exit
loop
}
if len(rom$)>0 or count>3 Else
=Str$(acc,1033)
end if
}
data "MMMCMXCIX", "LXXIIX", "MMXVII", "LXXIX", "CXCIX","MCMXCIX","MMMDCCCLXXXVIII"
data "CMXI","M","MCDXLIV","CCCC","IXV", "XLIXL","LXXIIX","IVM"
data "XXXIX", "XXXX", "XIXX","IVI", "XLIX","XCIX","XCIV","XLVIII"
return
}
RomanNumbers
</syntaxhighlight>

{{out}}
<pre style="height:30ex;overflow:scroll">
MMMCMXCIX=3999
LXXIIX=invalid
MMXVII=2017
LXXIX=79
CXCIX=199
MCMXCIX=1999
MMMDCCCLXXXVIII=3888
CMXI=911
M=1000
MCDXLIV=1444
CCCC=invalid
IXV=invalid
XLIXL=invalid
LXXIIX=invalid
IVM=invalid
XXXIX=39
XXXX=invalid
XIXX=invalid
IVI=invalid
XLIX=49
XCIX=99
XCIV=94
XLVIII=48

</pre >

=={{header|Maple}}==
<syntaxhighlight lang="maple">f := n -> convert(n, arabic):
seq(printf("%a\n", f(i)), i in [MCMXC, MMVIII, MDCLXVI]);</syntaxhighlight>
{{out}}
<pre>
1990
2008
1666
</pre>

=={{header|Mathematica}}/{{header|Wolfram Language}}==
<syntaxhighlight lang="mathematica">FromRomanNumeral["MMCDV"]</syntaxhighlight>
{{out}}
<pre>2405</pre>

=={{header|MATLAB}}==
<syntaxhighlight lang="matlab">function x = rom2dec(s)
% ROM2DEC converts Roman numbers to decimal

% store Roman digits values: I=1, V=5, X=10, L=50, C=100, D=500, M=1000
digitsValues = [0 0 100 500 0 0 0 0 1 0 0 50 1000 0 0 0 0 0 0 0 0 5 0 10 0 0];
% convert Roman number to array of values
values = digitsValues(s-'A'+1);
% change sign if next value is bigger
x = sum(values .* [sign(diff(-values)+eps),1]);

end</syntaxhighlight>
Here is a test:
<syntaxhighlight lang="matlab">romanNumbers = {'MMMCMXCIX', 'XLVIII', 'MMVIII'};
for n = 1 : numel(romanNumbers)
fprintf('%10s = %4d\n',romanNumbers{n}, rom2dec(romanNumbers{n}));
end</syntaxhighlight>
{{out}}
<pre>
MMMCMXCIX = 3999
XLVIII = 48
MMVIII = 2008
</pre>

=={{header|Mercury}}==
<syntaxhighlight lang="mercury">:- module test_roman.

:- interface.

:- import_module io.

:- pred main(io::di, io::uo) is det.

:- implementation.

:- import_module char.
:- import_module exception.
:- import_module int.
:- import_module list.
:- import_module string.

:- type conversion_error ---> not_a_roman_number.

:- func build_int(list(char), int, int) = int.
:- func from_roman(string) = int.
:- pred roman_to_int(char::in, int::out) is semidet.

from_roman(Roman) = Decimal :-
List = reverse(to_char_list(Roman)),
Decimal = build_int(List, 0, 0).

build_int([], LastValue, Accumulator) = LastValue + Accumulator.
build_int([Digit|Rest], LastValue, Accumulator) = Sum :-
( roman_to_int(Digit, Value) ->
( Value < LastValue ->
Sum = build_int(Rest, Value, Accumulator - LastValue)
; Sum = build_int(Rest, Value, Accumulator + LastValue) )
; throw(not_a_roman_number) ).

roman_to_int('I', 1).
roman_to_int('V', 5).
roman_to_int('X', 10).
roman_to_int('L', 50).
roman_to_int('C', 100).
roman_to_int('D', 500).
roman_to_int('M', 1000).

main(!IO) :-
command_line_arguments(Args, !IO),
foldl((pred(Arg::in, !.IO::di, !:IO::uo) is det :-
format("%s => %d\n", [s(Arg), i(from_roman(Arg))], !IO)),
Args, !IO).

:- end_module test_roman.</syntaxhighlight>

=={{header|Miranda}}==
<syntaxhighlight lang="miranda">main :: [sys_message]
main = [ Stdout (s ++ ": " ++ show (fromroman s) ++ "\n")
| s <- ["MCMXC", "MDCLXVI", "MMVII", "MMXXIII"]
]

fromroman :: [char]->num
fromroman = f
where f [] = 0
f [x] = r x
f (x:y:xs) = f (y:xs) - r x, if r x < r y
= f (y:xs) + r x, otherwise
r 'M' = 1000
r 'D' = 500
r 'C' = 100
r 'L' = 50
r 'X' = 10
r 'V' = 5
r 'I' = 1</syntaxhighlight>
{{out}}
<pre>MCMXC: 1990
MDCLXVI: 1666
MMVII: 2007
MMXXIII: 2023</pre>

=={{header|Modula-2}}==
<syntaxhighlight lang="modula2">MODULE RomanNumerals;
FROM InOut IMPORT WriteString, WriteCard, WriteLn;
FROM Strings IMPORT Length;

(* Convert given Roman numeral to binary *)
PROCEDURE DecodeRoman(s: ARRAY OF CHAR): CARDINAL;
VAR i, d, len, acc: CARDINAL;
PROCEDURE Digit(d: CHAR): CARDINAL;
BEGIN
CASE CHR( BITSET(ORD(d)) + BITSET{5} ) OF (* lowercase *)
'm': RETURN 1000;
| 'd': RETURN 500;
| 'c': RETURN 100;
| 'l': RETURN 50;
| 'x': RETURN 10;
| 'v': RETURN 5;
| 'i': RETURN 1;
ELSE
RETURN 0;
END;
END Digit;
BEGIN
len := Length(s);
acc := 0;
FOR i := 0 TO len-1 DO
d := Digit(s[i]);
IF d=0 THEN RETURN 0; END;
IF (i # len-1) AND (d < Digit(s[i+1])) THEN
acc := acc - d;
ELSE
acc := acc + d;
END;
END;
RETURN acc;
END DecodeRoman;

PROCEDURE Show(s: ARRAY OF CHAR);
BEGIN
WriteString(s);
WriteString(": ");
WriteCard(DecodeRoman(s), 0);
WriteLn();
END Show;

BEGIN
Show("MCMXC");
Show("MDCLXVI");
Show("mmvii");
Show("mmxxi");
END RomanNumerals.</syntaxhighlight>
{{out}}
<pre>MCMXC: 1990
MDCLXVI: 1666
mmvii: 2007
mmxxi: 2021</pre>

=={{header|Nanoquery}}==
{{trans|Java}}
<syntaxhighlight lang="nanoquery">def decodeSingle(letter)
if letter = "M"
return 1000
else if letter = "D"
return 500
else if letter = "C"
return 100
else if letter = "L"
return 50
else if letter = "X"
return 10
else if letter = "V"
return 5
else if letter = "I"
return 1
else
return 0
end
end

def decode(roman)
result = 0
uRoman = roman.toUpperCase()
for (i = 0) (i < len(uRoman) - 1) (i += 1)
if decodeSingle(uRoman[i]) < decodeSingle(uRoman[i + 1])
result -= decodeSingle(uRoman[i])
else
result += decodeSingle(uRoman[i])
end
end
result += decodeSingle(uRoman[len(uRoman) - 1])
return result
end

println decode("MCMXC")
println decode("MMVIII")
println decode("MDCLXVI")</syntaxhighlight>
{{out}}
<pre>1990
<pre>1990
2008
2008
Line 574: Line 5,064:


=={{header|NetRexx}}==
=={{header|NetRexx}}==
<lang NetRexx>/* NetRexx */
<syntaxhighlight lang="netrexx">/* NetRexx */
options replace format comments java crossref savelog symbols binary
options replace format comments java crossref savelog symbols binary


Line 622: Line 5,112:
end
end


return digit
return digit</syntaxhighlight>
{{out}}
</lang>
<pre>
'''Output:'''
<pre style="overflow:scroll">
MCMXC: 1990
MCMXC: 1990
MMVIII: 2008
MMVIII: 2008
Line 631: Line 5,120:
</pre>
</pre>


=={{header|OCaml}}==
=={{header|Nim}}==
{{trans|Python}}
<syntaxhighlight lang="nim">import tables


let rdecode = {'M': 1000, 'D': 500, 'C': 100, 'L': 50, 'X': 10, 'V': 5, 'I': 1}.toTable
<lang ocaml>let decimal_of_roman roman =

proc decode(roman: string): int =
for i in 0 ..< roman.high:
let (rd, rd1) = (rdecode[roman[i]], rdecode[roman[i+1]])
result += (if rd < rd1: -rd else: rd)
result += rdecode[roman[roman.high]]

for r in ["MCMXC", "MMVIII", "MDCLXVI"]:
echo r, " ", decode(r)</syntaxhighlight>

{{out}}
<pre>MCMXC 1990
MMVIII 2008
MDCLXVI 1666</pre>

=={{header|OCaml}}==
<syntaxhighlight lang="ocaml">let decimal_of_roman roman =
let arabic = ref 0 in
let arabic = ref 0 in
let lastval = ref 0 in
let lastval = ref 0 in
Line 659: Line 5,167:
Printf.printf " %d\n" (decimal_of_roman "MMVIII");
Printf.printf " %d\n" (decimal_of_roman "MMVIII");
Printf.printf " %d\n" (decimal_of_roman "MDCLXVI");
Printf.printf " %d\n" (decimal_of_roman "MDCLXVI");
;;</lang>
;;</syntaxhighlight>
=== Another implementation ===
Another implementation, a bit more OCaml-esque: no mutable variables, and a recursive function instead of a for loop.
{{works with|OCaml|4.03+}}
<syntaxhighlight lang="ocaml">
(* Scan the roman number from right to left. *)
(* When processing a roman digit, if the previously processed roman digit was
* greater than the current one, we must substract the latter from the current
* total, otherwise add it.
* Example:
* - MCMLXX read from right to left is XXLMCM
* the sum is 10 + 10 + 50 + 1000 - 100 + 1000 *)
let decimal_of_roman roman =
(* Use 'String.uppercase' for OCaml 4.02 and previous. *)
let rom = String.uppercase_ascii roman in
(* A simple association list. IMHO a Hashtbl is a bit overkill here. *)
let romans = List.combine ['I'; 'V'; 'X'; 'L'; 'C'; 'D'; 'M']
[1; 5; 10; 50; 100; 500; 1000] in
let compare x y =
if x < y then -1 else 1
in
(* Scan the string from right to left using index i, and keeping track of
* the previously processed roman digit in prevdig. *)
let rec doloop i prevdig =
if i < 0 then 0
else
try
let currdig = List.assoc rom.[i] romans in
(currdig * compare currdig prevdig) + doloop (i - 1) currdig
with
(* Ignore any incorrect roman digit and just process the next one. *)
Not_found -> doloop (i - 1) 0
in
doloop (String.length rom - 1) 0


(* Some simple tests. *)
let () =
let testit roman decimal =
let conv = decimal_of_roman roman in
let status = if conv = decimal then "PASS" else "FAIL" in
Printf.sprintf "[%s] %s\tgives %d.\tExpected: %d.\t"
status roman conv decimal
in
print_endline ">>> Usual roman numbers.";
print_endline (testit "MCMXC" 1990);
print_endline (testit "MMVIII" 2008);
print_endline (testit "MDCLXVI" 1666);
print_newline ();

print_endline ">>> Roman numbers with lower case letters are OK.";
print_endline (testit "McmXC" 1990);
print_endline (testit "MMviii" 2008);
print_endline (testit "mdCLXVI" 1666);
print_newline ();

print_endline ">>> Incorrect roman digits are ignored.";
print_endline (testit "McmFFXC" 1990);
print_endline (testit "MMviiiPPPPP" 2008);
print_endline (testit "mdCLXVI_WHAT_NOW" 1666);
print_endline (testit "2 * PI ^ 2" 1); (* The I in PI... *)
print_endline (testit "E = MC^2" 1100)
</syntaxhighlight>
Output:
<pre>
>>> Usual roman numbers.
[PASS] MCMXC gives 1990. Expected: 1990.
[PASS] MMVIII gives 2008. Expected: 2008.
[PASS] MDCLXVI gives 1666. Expected: 1666.

>>> Roman numbers with lower case letters are OK.
[PASS] McmXC gives 1990. Expected: 1990.
[PASS] MMviii gives 2008. Expected: 2008.
[PASS] mdCLXVI gives 1666. Expected: 1666.

>>> Incorrect roman digits are ignored.
[PASS] McmFFXC gives 1990. Expected: 1990.
[PASS] MMviiiPPPPP gives 2008. Expected: 2008.
[PASS] mdCLXVI_WHAT_NOW gives 1666. Expected: 1666.
[PASS] 2 * PI ^ 2 gives 1. Expected: 1.
[PASS] E = MC^2 gives 1100. Expected: 1100.
</pre>


=={{header|PARI/GP}}==
=={{header|PARI/GP}}==
<lang parigp>fromRoman(s)={
<syntaxhighlight lang="parigp">fromRoman(s)={
my(v=Vecsmall(s),key=vector(88),cur,t=0,tmp);
my(v=Vecsmall(s),key=vector(88),cur,t=0,tmp);
key[73]=1;key[86]=5;key[88]=10;key[76]=50;key[67]=100;key[68]=500;key[77]=1000;
key[73]=1;key[86]=5;key[88]=10;key[76]=50;key[67]=100;key[68]=500;key[77]=1000;
Line 678: Line 5,267:
);
);
t+cur
t+cur
};</lang>
};</syntaxhighlight>


=={{header|Perl}}==
=={{header|Perl}}==
<lang Perl>use 5.10.0;
<syntaxhighlight lang="perl">use 5.10.0;


{
sub from_roman {
my %trans = (
my @trans = (
M => 1000, CM => 900,
[M => 1000], [CM => 900],
D => 500, CD => 400,
[D => 500], [CD => 400],
C => 100, XC => 90,
[C => 100], [XC => 90],
L => 50, XL => 40,
[L => 50], [XL => 40],
X => 10, IX => 9,
[X => 10], [IX => 9],
V => 5, IV => 4,
[V => 5], [IV => 4],
I => 1,
[I => 1],
);
);


sub from_roman {
my ($r, $n) = @_;
while ($r =~ /(M|CM|D|CD|C|XC|L|XL|X|IX|V|IV|I)/ig) {
my $r = shift;
$n += $trans{uc $1}
my $n = 0;
foreach my $pair (@trans) {
my ($k, $v) = @$pair;
$n += $v while $r =~ s/^$k//i;
}
}
return $n
return $n
}
}
}


say "$_: ", from_roman($_) for qw(MCMXC MDCLXVI MMVIII);</lang>
say "$_: ", from_roman($_) for qw(MCMXC MDCLXVI MMVIII);</syntaxhighlight>
{{out}}
Output:<lang>MCMXC: 1990
<pre>MCMXC: 1990
MDCLXVI: 1666
MDCLXVI: 1666
MMVIII: 2008</lang>
MMVIII: 2008</pre>
=== Alternate ===
<syntaxhighlight lang="perl">#!/usr/bin/perl


use strict;
=={{header|Perl 6}}==
use warnings;
<lang perl6>sub rom-to-num($r) {

[+] gather $r.uc ~~ /
sub roman2decimal
^
[
{
(local $_, my $sum, my $zeros) = (shift, 0, '');
| 'M' { take 1000 }
$zeros .= 0 while
| 'CM' { take 900 }
| 'D' { take 500 }
$sum -= s/I(?=[VX])// - s/V// * 5 - s/I//g . $zeros,
tr/MDCLX/CLXVI/;
| 'CD' { take 400 }
return $sum;
| 'C' { take 100 }
}
| 'XC' { take 90 }

| 'L' { take 50 }
print s/$/ ": " . roman2decimal($_) /er while <DATA>;
| 'XL' { take 40 }

| 'X' { take 10 }
__DATA__
| 'IX' { take 9 }
MCMXC
| 'V' { take 5 }
MMVIII
| 'IV' { take 4 }
MDCLXVI</syntaxhighlight>
| 'I' { take 1 }
{{out}}
]+
<pre>
$
MCMXC: 1990
/;
MMVIII: 2008
MDCLXVI: 1666
</pre>
=== Another Alternate ===
<syntaxhighlight lang="perl">#!/usr/bin/perl

use strict;
use warnings;

sub roman2decimal
{
my $sum = 0;
$sum += $^R while $_[0] =~
/ M (?{1000})
| D (?{ 500})
| C (?{ 100}) (?= [MD] (?{-100}) )?
| L (?{ 50})
| X (?{ 10}) (?= [CL] (?{ -10}) )?
| V (?{ 5})
| I (?{ 1}) (?= [XV] (?{ -1}) )?
/gx;
return $sum;
}

print s/$/ ": " . roman2decimal($_) /er while <DATA>;

__DATA__
MCMXC
MMVIII
MDCLXVI</syntaxhighlight>
{{out}}
<pre>
MCMXC: 1990
MMVIII: 2008
MDCLXVI: 1666
</pre>

=={{header|Phix}}==
<!--(phixonline)-->
<syntaxhighlight lang="phix">
with javascript_semantics
function romanDec(string s)
integer res = 0, prev = 0
for i=length(s) to 1 by -1 do
integer rdx = find(upper(s[i]),"IVXLCDM"),
rn = power(10,floor((rdx-1)/2))
if even(rdx) then rn *= 5 end if
res += iff(rn<prev?-rn:rn)
prev = rn
end for
return {s,res} -- (for output)
end function

?apply({"MCMXC","MMVIII","MDCLXVI"},romanDec)
</syntaxhighlight>
{{out}}
<pre>
{{"MCMXC",1990},{"MMVIII",2008},{"MDCLXVI",1666}}
</pre>
=== cheating slightly ===
<syntaxhighlight lang="phix">
with javascript_semantics
requires("1.0.5")
function romanDec(string s)
return {s,scanf(s,"%R")[1][1]}
end function
</syntaxhighlight>
same output, if applied the same way as above, error handling omitted

=={{header|Phixmonti}}==
<syntaxhighlight lang="phixmonti">def romanDec /# s -- n #/
0 >ps 0 >ps
( ( "M" 1000 ) ( "D" 500 ) ( "C" 100 ) ( "L" 50 ) ( "X" 10 ) ( "V" 5 ) ( "I" 1 ) )
swap upper reverse
len while
pop rot rot tochar getd
if
dup ps> < if 0 swap - endif
dup ps> + >ps
>ps
swap
else
"Error: " print ? ""
endif
len
endwhile
drop drop
ps> drop ps>
enddef

/# usage example: "MMXX" romanDec ? (show 2020) #/</syntaxhighlight>

More traditional solution:

<syntaxhighlight lang="phixmonti">"MDCLXVI" var romans
( 1000 500 100 50 10 5 1 ) var decmls

def romanDec /# s -- n #/
0 var prev
0 var res
upper
( len 1 -1 ) for
get
romans swap find nip
dup if
decmls swap get nip
dup prev < if 0 swap - endif
dup res + var res
var prev
else
"Error" ? 0 var res exitfor
endif
endfor
drop
res
enddef</syntaxhighlight>

=={{header|PHP}}==
<syntaxhighlight lang="php"><?php
/**
* @author Elad Yosifon
*/
$roman_to_decimal = array(
'I' => 1,
'V' => 5,
'X' => 10,
'L' => 50,
'C' => 100,
'D' => 500,
'M' => 1000,
);

/**
* @param $number
* @return int
*/
function roman2decimal($number)
{
global $roman_to_decimal;

// breaks the string into an array of chars
$digits = str_split($number);
$lastIndex = count($digits)-1;
$sum = 0;

foreach($digits as $index => $digit)
{
if(!isset($digits[$index]))
{
continue;
}

if(isset($roman_to_decimal[$digit]))
{
if($index < $lastIndex)
{
$left = $roman_to_decimal[$digits[$index]];
$right = $roman_to_decimal[$digits[$index+1]];
if($left < $right)
{
$sum += ($right - $left);
unset($digits[$index+1],$left, $right);
continue;
}
unset($left, $right);
}
}
$sum += $roman_to_decimal[$digit];
}

return $sum;
}
}


/*============= OUTPUT =============*/
say "$_ => &rom-to-num($_)" for <MCMXC MDCLXVI MMVIII>;</lang>
header('Content-Type: text/plain');
Output:

<pre>MCMXC => 1990
$tests = array(
MDCLXVI => 1666
"I" => array(roman2decimal('I'), 1),
MMVIII => 2008</pre>
"II" => array(roman2decimal('II'), 2),
"III" => array(roman2decimal('III'), 3),
"IV" => array(roman2decimal('IV'), 4),
"V" => array(roman2decimal('V'), 5),
"VI" => array(roman2decimal('VI'), 6),
"VII" => array(roman2decimal('VII'), 7),
"IX" => array(roman2decimal('IX'), 9),
"X" => array(roman2decimal('X'), 10),
"XI" => array(roman2decimal('XI'), 11),
"XIV" => array(roman2decimal('XIV'), 14),
"XV" => array(roman2decimal('XV'), 15),
"XVI" => array(roman2decimal('XVI'), 16),
"XVIV" => array(roman2decimal('XVIV'), 19),
"XIX" => array(roman2decimal('XIX'), 19),
"MDCLXVI" => array(roman2decimal('MDCLXVI'), 1666),
"MCMXC" => array(roman2decimal('MCMXC'), 1990),
"MMVIII" => array(roman2decimal('MMVIII'), 2008),
"MMMCLIX" => array(roman2decimal('MMMCLIX'), 3159),
"MCMLXXVII" => array(roman2decimal('MCMLXXVII'), 1977),
);


foreach($tests as $key => $value)
{
echo "($key == {$value[0]}) => " . ($value[0] === $value[1] ? "true" : "false, should be {$value[1]}.") . "\n";
}</syntaxhighlight>
{{out}}
<pre>
(I == 1) => true
(II == 2) => true
(III == 3) => true
(IV == 4) => true
(V == 5) => true
(VI == 6) => true
(VII == 7) => true
(IX == 9) => true
(X == 10) => true
(XI == 11) => true
(XIV == 14) => true
(XV == 15) => true
(XVI == 16) => true
(XVIV == 19) => true
(XIX == 19) => true
(MDCLXVI == 1666) => true
(MCMXC == 1990) => true
(MMVIII == 2008) => true
(MMMCLIX == 3159) => true
(MCMLXXVII == 1977) => true
</pre>

=={{header|Picat}}==
<syntaxhighlight lang="picat">go =>
List = ["IV",
"XLII",
"M",
"MCXI",
"CMXI",
"MCM",
"MCMXC",
"MMVIII",
"MMIX",
"MCDXLIV",
"MDCLXVI",
"MMXII"],
foreach(R in List)
printf("%-8s: %w\n", R, roman_decode(R))
end,
nl.


roman_decode(Str) = Res =>
if Str == "" then
Res := ""
else
D = new_map(findall((R=D), roman(R,D))),
Res = 0,
Old = 0,
foreach(S in Str)
N = D.get(S),
% Fix for the Roman convention that XC = 90, not 110.
if Old > 0, N > Old then
Res := Res - 2*Old
end,
Res := Res + N,
Old := N
end
end.

roman('I', 1).
roman('V', 5).
roman('X', 10).
roman('L', 50).
roman('C', 100).
roman('D', 500).
roman('M', 1000).</syntaxhighlight>

{{out}}
<pre>IV : 4
XLII : 42
M : 1000
MCXI : 1111
CMXI : 911
MCM : 1900
MCMXC : 1990
MMVIII : 2008
MMIX : 2009
MCDXLIV : 1444
MDCLXVI : 1666
MMXII : 2012</pre>



=={{header|PicoLisp}}==
=={{header|PicoLisp}}==
<lang PicoLisp>(de roman2decimal (Rom)
<syntaxhighlight lang="picolisp">(de roman2decimal (Rom)
(let L (replace (chop Rom) 'M 1000 'D 500 'C 100 'L 50 'X 10 'V 5 'I 1)
(let L (replace (chop Rom) 'M 1000 'D 500 'C 100 'L 50 'X 10 'V 5 'I 1)
(sum '((A B) (if (>= A B) A (- A))) L (cdr L)) ) )</lang>
(sum '((A B) (if (>= A B) A (- A))) L (cdr L)) ) )</syntaxhighlight>
Test:
Test:
<pre>: (roman2decimal "MCMXC")
<pre>: (roman2decimal "MCMXC")
Line 749: Line 5,627:
-> 1666</pre>
-> 1666</pre>


=={{header|Prolog}}==
=={{header|PL/I}}==
<syntaxhighlight lang="pl/i">
test_decode: procedure options (main); /* 28 January 2013 */
declare roman character (20) varying;


do roman = 'i', 'ii', 'iii', 'iv', 'v', 'vi', 'vii', 'viii', 'iix',
===SWI-Prolog and clpfd===
'ix', 'x', 'xi', 'xiv', 'MCMLXIV', 'MCMXC', 'MDCLXVI',
Works with SWI-Prolog and library clpfd.<BR>
'MIM', 'MM', 'MMXIII';
Library clpfd assures that the program works in both managements : Roman towards Arabic and Arabic towards Roman.<BR>
put skip list (roman, decode(roman));
It's 99% the same code !
end;
<lang Prolog>roman :-
LA = [ _ , 2010, _, 1449, _],
LR = ['MDCCLXXXIX', _ , 'CX', _, 'MDCLXVI'],
maplist(roman, LA, LR),


decode: procedure (roman) returns (fixed(15));
% change here !
declare roman character (*) varying;
maplist(my_print,LR, LA).
declare (current, previous) character (1);
declare n fixed (15);
declare i fixed binary;


previous = ''; n = 0;
do i = length(roman) to 1 by -1;
current = substr(roman, i, 1);
if digit_value(current) < digit_value(previous) then
n = n - digit_value(current);
else if digit_value(current) > digit_value(previous) then
do;
n = n + digit_value(current);
previous = current;
end;
else
n = n + digit_value(current);
end;
return (n);
end decode;


digit_value: procedure (roman_char) returns (fixed);
roman(A, R) :-
declare roman_char character(1);
A #> 0,
select (roman_char);
roman(A, [u, t, h, th], LR, []),
when ('M', 'm') return (1000);
label([A]),
when ('D', 'd') return (500);
parse_Roman(CR, LR, []),
when ('C', 'c') return (100);
atom_chars(R, CR).
when ('L', 'l') return (50);
when ('X', 'x') return (10);
when ('V', 'v') return (5);
when ('I', 'i') return (1);
otherwise return (0);
end;
end digit_value;


end test_decode;
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
</syntaxhighlight>
% using DCG
<pre>
i 1
roman(0, []) --> [].
ii 2
iii 3
iv 4
v 5
vi 6
vii 7
viii 8
iix 8
ix 9
x 10
xi 11
xiv 14
MCMLXIV 1964
MCMXC 1990
MDCLXVI 1666
MIM 1999
MM 2000
MMXIII 2013
</pre>


=={{header|PL/M}}==
roman(N, [H | T]) -->
<syntaxhighlight lang="plm">100H:
{N1 #= N / 10,
/* CP/M CALLS */
N2 #= N mod 10},
BDOS: PROCEDURE (FN, ARG); DECLARE FN BYTE, ARG ADDRESS; GO TO 5; END BDOS;
roman(N1, T),
EXIT: PROCEDURE; CALL BDOS(0,0); END EXIT;
unity(N2, H).
PRINT: PROCEDURE (S); DECLARE S ADDRESS; CALL BDOS(9,S); END PRINT;


/* CP/M COMMAND LINE ARGUMENT */
unity(1, u) --> ['I'].
DECLARE ARG$LPTR ADDRESS INITIAL (80H), ARG$LEN BASED ARG$LPTR BYTE;
unity(1, t) --> ['X'].
DECLARE ARG$PTR ADDRESS INITIAL (81H), ARG BASED ARG$PTR BYTE;
unity(1, h) --> ['C'].
unity(1, th)--> ['M'].


/* CONVERT ROMAN NUMERAL TO BINARY */
unity(4, u) --> ['IV'].
READ$ROMAN: PROCEDURE (RP) ADDRESS;
unity(4, t) --> ['XL'].
DECLARE DIGITS (7) BYTE INITIAL ('MDCLXVI');
unity(4, h) --> ['CD'].
DECLARE VALUES (7) ADDRESS INITIAL (1000,500,100,50,10,5,1);
unity(4, th)--> ['MMMM'].
DECLARE (RP, V, DVAL) ADDRESS, R BASED RP BYTE;
V = 0;
GET$DIGIT: PROCEDURE (D) ADDRESS;
DECLARE (D, I) BYTE;
DO I = 0 TO LAST(DIGITS);
IF DIGITS(I) = D THEN RETURN VALUES(I);
END;
RETURN 0; /* NOT FOUND */
END GET$DIGIT;
DO WHILE R <> '$';
DVAL = GET$DIGIT(R);
IF DVAL = 0 THEN RETURN 0; /* ERROR */
RP = RP + 1;
IF GET$DIGIT(R) > DVAL THEN
V = V - DVAL; /* SUBTRACTIVE PRINCIPLE */
ELSE
V = V + DVAL;
END;
RETURN V;
END READ$ROMAN;


/* PRINT BINARY NUMBER AS DECIMAL */
unity(5, u) --> ['V'].
PRINT$NUMBER: PROCEDURE (N);
unity(5, t) --> ['L'].
DECLARE S (6) BYTE INITIAL ('.....$');
unity(5, h) --> ['D'].
DECLARE (N, P) ADDRESS, C BASED P BYTE;
unity(5, th)--> ['MMMMM'].
P = .S(5);
DIGIT:
P = P - 1;
C = N MOD 10 + '0';
N = N / 10;
IF N > 0 THEN GO TO DIGIT;
CALL PRINT(P);
END PRINT$NUMBER;


IF ARG$LEN = 0 THEN DO;
unity(9, u) --> ['IX'].
CALL PRINT(.'NO INPUT$');
unity(9, t) --> ['XC'].
CALL EXIT;
unity(9, h) --> ['CM'].
END;
unity(9, th)--> ['MMMMMMMMM'].


ARG(ARG$LEN) = '$'; /* TERMINATE ARGUMENT STRING */
unity(0, _) --> [].
CALL PRINT(.ARG(1)); /* PRINT ROMAN NUMERAL */
CALL PRINT(.': $');
CALL PRINT$NUMBER(READ$ROMAN(.ARG(1))); /* CONVERT AND PRINT VALUE */
CALL EXIT;
EOF</syntaxhighlight>


{{out}}


<pre>A>ROMAN MCMXC
unity(V, U)-->
MCMXC: 1990
{V #> 5,
A>ROMAN MDCLXVI
V1 #= V - 5},
MDCLXVI: 1666
unity(5, U),
A>ROMAN MMVII
unity(V1, U).
MMVII: 2007
A>ROMAN MMXXI
MMXXI: 2021</pre>


=={{header|PL/SQL}}==
unity(V, U) -->
{V #> 1, V #< 4,
V1 #= V-1},
unity(1, U),
unity(V1, U).


<syntaxhighlight lang="pl/sql">
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
/*****************************************************************
% Extraction of roman "lexeme"
* $Author: Atanas Kebedjiev $
parse_Roman(['C','M'|T]) -->
*****************************************************************
['CM'],
* PL/SQL code can be run as anonymous block.
parse_Roman(T).
* To test, execute the whole script or create the functions and then e.g. 'select rdecode('2012') from dual;
* Please note that task definition does not describe fully some current rules, such as
* * subtraction - IX XC CM are the valid subtraction combinations
* * A subtraction character cannot be repeated: 8 is expressed as VIII and not as IIX
* * V L and D cannot be used for subtraction
* * Any numeral cannot be repeated more than 3 times: 1910 should be MCMX and not MDCCCCX
* Code below does not validate the Roman numeral itself and will return a result even for a non-compliant number
* E.g. both MCMXCIX and IMM will return 1999 but the first one is the correct notation
*/


DECLARE
parse_Roman(['C','D'|T]) -->
['CD'],
parse_Roman(T).


FUNCTION rvalue(c IN CHAR) RETURN NUMBER IS
parse_Roman(['X','C'| T]) -->
i INTEGER;
['XC'],
BEGIN
parse_Roman(T).
i := 0;
CASE (c)
when 'M' THEN i := 1000;
when 'D' THEN i := 500;
when 'C' THEN i := 100;
when 'L' THEN i := 50;
when 'X' THEN i := 10;
when 'V' THEN i := 5;
when 'I' THEN i := 1;
END CASE;
RETURN i;
END;




FUNCTION decode(rn IN VARCHAR2) RETURN NUMBER IS
parse_Roman(['X','L'| T]) -->
i INTEGER;
['XL'],
l INTEGER;
parse_Roman(T).
cr CHAR; -- current Roman numeral as substring from r
cv INTEGER; -- value of current Roman numeral


gr CHAR; -- next Roman numeral
gv NUMBER; -- value of the next numeral;


dv NUMBER; -- decimal value to return
parse_Roman(['I','X'| T]) -->
BEGIN
['IX'],
l := length(rn);
parse_Roman(T).
i := 1;
dv := 0;
while (i <= l)
LOOP
cr := substr(rn,i,1);
cv := rvalue(cr);


/* Look for a larger numeral in next position, like IV or CM
The number to subtract should be at least 1/10th of the bigger number
CM and XC are valid, but IC and XM are not */
IF (i < l) THEN
gr := substr(rn,i+1,1);
gv := rvalue(gr);
IF (cv < gv ) THEN
dv := dv - cv;
ELSE
dv := dv + cv;
END IF;
ELSE
dv := dv + cv;
END IF; -- need to add the last value unconditionally


i := i + 1;
parse_Roman(['I','V'| T]) -->
END LOOP;
['IV'],
parse_Roman(T).


RETURN dv;
parse_Roman([H | T]) -->
[H],
parse_Roman(T).


END;


BEGIN
parse_Roman([]) -->
[].


DBMS_OUTPUT.PUT_LINE ('MMXII = ' || rdecode('MMXII')); -- 2012
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
DBMS_OUTPUT.PUT_LINE ('MCMLI = ' || rdecode('MCMLI')); -- 1951
% change here !
DBMS_OUTPUT.PUT_LINE ('MCMLXXXVII = ' || rdecode('MCMLXXXVII')); -- 1987
my_print(A, R) :-
DBMS_OUTPUT.PUT_LINE ('MDCLXVI = ' || rdecode('MDCLXVI')); -- 1666
format('~w in arabic is ~w~n', [A, R]).
DBMS_OUTPUT.PUT_LINE ('MCMXCIX = ' || rdecode('MCMXCIX')); -- 1999
</lang>

Output :
END;
<pre> ?- roman.
</syntaxhighlight>
MDCCLXXXIX in arabic is 1789

MMX in arabic is 2010
=={{header|PowerShell}}==
CX in arabic is 110
<syntaxhighlight lang="powershell">
MCDXLIX in arabic is 1449
Filter FromRoman {
MDCLXVI in arabic is 1666
$output = 0
MCMXCIV in arabic is 1994
true
if ($_ -notmatch '^(M{1,3}|)(CM|CD|D?C{0,3}|)(XC|XL|L?X{0,3}|)(IX|IV|V?I{0,3}|)$') {
throw 'Incorrect format'
}
$current = 1000
$subtractor = 'M'
$whole = $False
$roman = $_
'C','D','X','L','I','V',' ' `
| %{
if ($whole = !$whole) {
$current /= 10
$subtractor = $_ + $subtractor[0]
$_ = $subtractor[1]
}
else {
$subtractor = $subtractor[0] + $_
}
if ($roman -match $subtractor) {
$output += $current * (4,9)[$whole]
$roman = $roman -replace $subtractor,''
}
if ($roman -match ($_ + '{1,3}')) {
$output += $current * (5,10)[$whole] * $Matches[0].Length
}
}
$output
}
</syntaxhighlight>
<syntaxhighlight lang="powershell">
'XIX','IV','','MMCDLXXIX','MMMI' | FromRoman
</syntaxhighlight>
{{Out}}
<pre>
19
4
0
2479
3001
</pre>
</pre>


===SWI Prolog===
=={{header|Prolog}}==
<syntaxhighlight lang="prolog">decode_digit(i, 1).
{{untested|Prolog}}
decode_digit(v, 5).
{{works with|SWI Prolog}}
decode_digit(x, 10).
<lang prolog>char_to_num('M', 1000).
char_to_num('D', 500).
decode_digit(l, 50).
char_to_num('C', 100).
decode_digit(c, 100).
char_to_num('L', 50).
decode_digit(d, 500).
char_to_num('X', 10).
decode_digit(m, 1000).
char_to_num('V', 5).
char_to_num('I', 1).
char_to_num(_, 0).


decode_string(Sum, _, [], Sum).
unroman('', 0).


decode_string(LastSum, LastValue, [Digit|Rest], NextSum) :-
unroman(Roman, X) :- string_length(Roman, Length), RestLen is Length - 1, NextLen is Length - 2,
decode_digit(Digit, Value),
sub_string(Roman, 1, 1, RestLen, First), sub_string(Roman, 2, 1, NextLen, Next),
Value < LastValue,
sub_string(Roman, 2, RestLen, 0, Rest), char_to_num(First, FirstNum), char_to_num(Next, NextNum),
Sum is LastSum - Value,
FirstNum >= NextNum, unroman(Rest, RestNum), X is RestNum + FirstNum.
decode_string(Sum, Value, Rest, NextSum).


decode_string(LastSum, LastValue, [Digit|Rest], NextSum) :-
unroman(Roman, X) :- string_length(Roman, Length), RestLen is Length - 1, NextLen is Length - 2,
decode_digit(Digit, Value),
sub_string(Roman, 1, 1, RestLen, First), sub_string(Roman, 2, 1, NextLen, Next),
Value >= LastValue,
sub_string(Roman, 2, RestLen, 0, Rest), char_to_num(First, FirstNum), char_to_num(Next, NextNum),
Sum is LastSum + Value,
FirstNum < NextNum, unroman(Rest, RestNum), X is RestNum - FirstNum.</lang>
decode_string(Sum, Value, Rest, NextSum).
=={{header|PureBasic}}==
<lang PureBasic>Procedure romanDec(roman.s)
Protected i, n, lastval, arabic
For i = Len(roman) To 1 Step -1
Select UCase(Mid(roman, i, 1))
Case "M"
n = 1000
Case "D"
n = 500
Case "C"
n = 100
Case "L"
n = 50
Case "X"
n = 10
Case "V"
n = 5
Case "I"
n = 1
Default
n = 0
EndSelect
If (n < lastval)
arabic - n
Else
arabic + n
EndIf
lastval = n
Next
ProcedureReturn arabic
EndProcedure


decode_string(Atom, Value) :-
If OpenConsole()
atom_chars(Atom, String),
PrintN(Str(romanDec("MCMXCIX"))) ;1999
reverse(String, [Last|Rest]),
PrintN(Str(romanDec("MDCLXVI"))) ;1666
decode_digit(Last, Start),
PrintN(Str(romanDec("XXV"))) ;25
decode_string(Start, Start, Rest, Value).
PrintN(Str(romanDec("CMLIV"))) ;954

PrintN(Str(romanDec("MMXI"))) ;2011
test :-
decode_string(mcmxc, 1990),
Print(#CRLF$ + #CRLF$ + "Press ENTER to exit"): Input()
decode_string(mmviii, 2008),
CloseConsole()
decode_string(mdclxvi, 1666).</syntaxhighlight>
EndIf</lang>
The program above contains its own test predicate.
Sample output:
The respective goal succeeds.
<pre>1999
Therefore the test passes.
1666
25
954
2011</pre>


=={{header|Python}}==
=={{header|Python}}==
===Imperative===
<lang python>_rdecode = dict(zip('MDCLXVI', (1000, 500, 100, 50, 10, 5, 1)))
<syntaxhighlight lang="python">_rdecode = dict(zip('MDCLXVI', (1000, 500, 100, 50, 10, 5, 1)))


def decode( roman ):
def decode( roman ):
Line 956: Line 5,958:
if __name__ == '__main__':
if __name__ == '__main__':
for r in 'MCMXC MMVIII MDCLXVI'.split():
for r in 'MCMXC MMVIII MDCLXVI'.split():
print( r, decode(r) )</lang>
print( r, decode(r) )</syntaxhighlight>
{{out}}

;Sample output:
<pre>MCMXC 1990
<pre>MCMXC 1990
MMVIII 2008
MMVIII 2008
MDCLXVI 1666</pre>
MDCLXVI 1666</pre>


Another version, for me clearer logic:
Another version, which I believe has clearer logic:
<lang python>roman_values = (('I',1), ('IV',4), ('V',5), ('IX',9),('X',10),('XL',40),('L',50),('XC',90),('C',100),
<syntaxhighlight lang="python">roman_values = (('I',1), ('IV',4), ('V',5), ('IX',9),('X',10),('XL',40),('L',50),('XC',90),('C',100),
('CD', 400), ('D', 500), ('CM', 900), ('M',1000))
('CD', 400), ('D', 500), ('CM', 900), ('M',1000))


Line 978: Line 5,979:
for value in "MCMXC", "MMVIII", "MDCLXVI":
for value in "MCMXC", "MMVIII", "MDCLXVI":
print('%s = %i' % (value, roman_value(value)))
print('%s = %i' % (value, roman_value(value)))
</syntaxhighlight>

{{out}}
""" Output:
<pre>
MCMXC = 1990
MCMXC = 1990
MMVIII = 2008
MMVIII = 2008
MDCLXVI = 1666
MDCLXVI = 1666
</pre><!--[[User:Tonyjv|Tonyjv]] 16:29, 20 September 2011 (UTC)--> <!-- contributions not normally signed visually; info is in history -->
"""

</lang>--[[User:Tonyjv|Tonyjv]] 16:29, 20 September 2011 (UTC)

===Declarative===
Less clear, but a 'one liner':
<syntaxhighlight lang="python">numerals = { 'M' : 1000, 'D' : 500, 'C' : 100, 'L' : 50, 'X' : 10, 'V' : 5, 'I' : 1 }
def romannumeral2number(s):
return reduce(lambda x, y: -x + y if x < y else x + y, map(lambda x: numerals.get(x, 0), s.upper()))</syntaxhighlight>


Or, defining '''intFromRoman''' as a fold or reduction,
and annotating a little more fully:
{{Trans|Haskell}}
{{Works with|Python|3}}
<syntaxhighlight lang="python">'''Roman numerals decoded'''

from operator import mul
from functools import reduce
from collections import defaultdict
from itertools import accumulate, chain, cycle


# intFromRoman :: String -> Maybe Int
def intFromRoman(s):
'''Just the integer represented by a Roman
numeral string, or Nothing if any
characters are unrecognised.
'''
dct = defaultdict(
lambda: None,
zip(
'IVXLCDM',
accumulate(chain([1], cycle([5, 2])), mul)
)
)

def go(mb, x):
'''Just a letter value added to or
subtracted from a total, or Nothing
if no letter value is defined.
'''
if None in (mb, x):
return None
else:
r, total = mb
return x, total + (-x if x < r else x)

return bindMay(reduce(
go,
[dct[k.upper()] for k in reversed(list(s))],
(0, 0)
))(snd)


# ------------------------- TEST -------------------------
def main():
'''Testing a sample of dates.'''

print(
fTable(__doc__ + ':\n')(str)(
maybe('(Contains unknown character)')(str)
)(
intFromRoman
)([
"MDCLXVI", "MCMXC", "MMVIII",
"MMXVI", "MMXVIII", "MMZZIII"
])
)


# ----------------------- GENERIC ------------------------

# bindMay (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
def bindMay(m):
'''Injection operator for the Maybe monad.
If m is Nothing, it is passed straight through.
If m is Just(x), the result is an application
of the (a -> Maybe b) function (mf) to x.'''
return lambda mf: (
m if None is m else mf(m)
)


# maybe :: b -> (a -> b) -> Maybe a -> b
def maybe(v):
'''Either the default value v, if m is Nothing,
or the application of f to x,
where m is Just(x).
'''
return lambda f: lambda m: v if None is m else (
f(m)
)


# snd :: (a, b) -> b
def snd(ab):
'''Second member of a pair.'''
return ab[1]


# ---------------------- FORMATTING ----------------------

# fTable :: String -> (a -> String) ->
# (b -> String) -> (a -> b) -> [a] -> String
def fTable(s):
'''Heading -> x display function ->
fx display function -> f -> xs -> tabular string.
'''
def go(xShow, fxShow, f, xs):
ys = [xShow(x) for x in xs]
w = max(map(len, ys))
return s + '\n' + '\n'.join(map(
lambda x, y: (
f'{y.rjust(w, " ")} -> {fxShow(f(x))}'
),
xs, ys
))
return lambda xShow: lambda fxShow: lambda f: (
lambda xs: go(xShow, fxShow, f, xs)
)


# MAIN ---
if __name__ == '__main__':
main()</syntaxhighlight>
{{Out}}
<pre>Roman numerals decoded:

MDCLXVI -> 1666
MCMXC -> 1990
MMVIII -> 2008
MMXVI -> 2016
MMXVIII -> 2018
MMZZIII -> (Contains unknown character)</pre>

=={{header|Quackery}}==
<syntaxhighlight lang="quackery"> [ 2dup <
if
[ dip
[ 2 * - ]
dup ]
nip dup
rot + swap ] is roman ( t p n --> t p )
[ 1 roman ] is I ( t p --> t p )
[ 5 roman ] is V ( t p --> t p )
[ 10 roman ] is X ( t p --> t p )
[ 50 roman ] is L ( t p --> t p )
[ 100 roman ] is C ( t p --> t p )
[ 500 roman ] is D ( t p --> t p )
[ 1000 roman ] is M ( t p --> t p )
[ 0 1000 rot
$ "" swap
witheach
[ space join
join ]
quackery
drop ] is ->arabic ( $ --> n )
$ " MCMXC" dup echo$ say " = " ->arabic echo cr
$ " MMVIII" dup echo$ say " = " ->arabic echo cr
$ "MDCLXVI" dup echo$ say " = " ->arabic echo cr
cr
$ "I MIX VIVID MILD MIMIC"
dup echo$ say " = " ->arabic echo cr
</syntaxhighlight>
{{Out}}
<pre> MCMXC = 1990
MMVIII = 2008
MDCLXVI = 1666

I MIX VIVID MILD MIMIC = 3063</pre>



=={{header|R}}==
===version 1===
Modelled along the lines of other decode routines on this page, but using a vectorised approach
<syntaxhighlight lang="r">romanToArabic <- function(roman) {
romanLookup <- c(I=1L, V=5L, X=10L, L=50L, C=100L, D=500L, M=1000L)
rSplit <- strsplit(toupper(roman), character(0)) # Split input vector into characters
toArabic <- function(item) {
digits <- romanLookup[item]
if (length(digits) > 1L) {
smaller <- (digits[-length(digits)] < digits[-1L])
digits[smaller] <- - digits[smaller]
}
sum(digits)
}
vapply(rSplit, toArabic, integer(1))
}</syntaxhighlight>

Example usage:
<syntaxhighlight lang="r">romanToArabic(c("MCMXII", "LXXXVI"))</syntaxhighlight>

===version 2===
Using built-in functionality in R

<syntaxhighlight lang="r">as.integer(as.roman(c("MCMXII", "LXXXVI"))</syntaxhighlight>

=={{header|Racket}}==
<syntaxhighlight lang="racket">#lang racket
(define (decode/roman number)
(define letter-values
(map cons '(#\M #\D #\C #\L #\X #\V #\I) '(1000 500 100 50 10 5 1)))
(define (get-value letter)
(cdr (assq letter letter-values)))
(define lst (map get-value (string->list number)))
(+ (last lst)
(for/fold ((sum 0))
((i (in-list lst)) (i+1 (in-list (cdr lst))))
(+ sum
(if (> i+1 i)
(- i)
i)))))

(map decode/roman '("MCMXC" "MMVIII" "MDCLXVI"))
;-> '(1990 2008 1666)</syntaxhighlight>

=={{header|Raku}}==
(formerly Perl 6)
A non-validating version:
<syntaxhighlight lang="raku" line>sub rom-to-num($r) {
[+] gather $r.uc ~~ /
^
[
| M { take 1000 }
| CM { take 900 }
| D { take 500 }
| CD { take 400 }
| C { take 100 }
| XC { take 90 }
| L { take 50 }
| XL { take 40 }
| X { take 10 }
| IX { take 9 }
| V { take 5 }
| IV { take 4 }
| I { take 1 }
]+
$
/;
}

say "$_ => &rom-to-num($_)" for <MCMXC MDCLXVI MMVIII>;</syntaxhighlight>
{{out}}
<pre>MCMXC => 1990
MDCLXVI => 1666
MMVIII => 2008</pre>
A validating version. Also handles older forms such as 'IIXX' and "IIII".
<syntaxhighlight lang="raku" line>sub rom-to-num($r) {
[+] gather $r.uc ~~ /
^
( (C*)M { take 1000 - 100 * $0.chars } )*
( (C*)D { take 500 - 100 * $0.chars } )?
( (X*)C { take 100 - 10 * $0.chars } )*
( (X*)L { take 50 - 10 * $0.chars } )?
( (I*)X { take 10 - $0.chars } )*
( (I*)V { take 5 - $0.chars } )?
( I { take 1 } )*
[ $ || { return NaN } ]
/;
}

say "$_ => ", rom-to-num($_) for <MCMXC mdclxvi MMViii IIXX ILL>;</syntaxhighlight>
{{out}}
<pre>MCMXC => 1990
mdclxvi => 1666
MMViii => 2008
IIXX => 18
ILL => NaN</pre>

=={{header|Red}}==
===version 1===

<syntaxhighlight lang="red">Red [
Purpose: "Arabic <-> Roman numbers converter"
Author: "Didier Cadieu"
Date: "07-Oct-2016"
]

table-r2a: reverse [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"]

roman-to-arabic: func [r [string!] /local a b e] [
a: 0
parse r [any [b: ["I" ["V" | "X" | none] | "X" ["L" | "C" | none] | "C" ["D" | "M" | none] | "V" | "L" | "D" | "M"] e: (a: a + select table-r2a copy/part b e)]]
a
]

; Example usage:
print roman-to-arabic "XXXIII"
print roman-to-arabic "MDCCCLXXXVIII"
print roman-to-arabic "MMXVI"
</syntaxhighlight>

=={{header|REFAL}}==
<syntaxhighlight lang="refal">$ENTRY Go {
= <Prout <RomanDecode 'MCMXC'>>
<Prout <RomanDecode 'MMVIII'>>
<Prout <RomanDecode 'MDCLXVI'>>;
};

RomanDecode {
= 0;
e.D, <Upper e.D>: {
'M' e.R = <+ 1000 <RomanDecode e.R>>;
'CM' e.R = <+ 900 <RomanDecode e.R>>;
'D' e.R = <+ 500 <RomanDecode e.R>>;
'CD' e.R = <+ 400 <RomanDecode e.R>>;
'C' e.R = <+ 100 <RomanDecode e.R>>;
'XC' e.R = <+ 90 <RomanDecode e.R>>;
'L' e.R = <+ 50 <RomanDecode e.R>>;
'XL' e.R = <+ 40 <RomanDecode e.R>>;
'X' e.R = <+ 10 <RomanDecode e.R>>;
'IX' e.R = <+ 9 <RomanDecode e.R>>;
'V' e.R = <+ 5 <RomanDecode e.R>>;
'IV' e.R = <+ 4 <RomanDecode e.R>>;
'I' e.R = <+ 1 <RomanDecode e.R>>;
};
};</syntaxhighlight>
{{out}}
<pre>1990
2008
1666</pre>


=={{header|REXX}}==
=={{header|REXX}}==
===version 1===
<lang REXX>/* Rexx */
{{Trans|NetRexx}}
{{Works with|Regina}}
{{Works with|ooRexx}}

<syntaxhighlight lang="rexx">/* Rexx */


Do
Do
Line 1,049: Line 6,379:
Return digit
Return digit
End
End
Exit</syntaxhighlight>
Exit
{{out}}
</lang>
<pre>
'''Output:'''
<pre style="overflow:scroll">
MCMXC: 1990
MCMXC: 1990
MMVIII: 2008
MMVIII: 2008
MDCLXVI: 1666
MDCLXVI: 1666
</pre>
</pre>

===version 2===
This version of the (above) REXX program:
:::* &nbsp; removes 3 sets of superfluous &nbsp; '''do──end''' &nbsp; statements
:::* &nbsp; removes dead code (3 REXX statements that can't be executed)
:::* &nbsp; replaced &nbsp; '''substr(xxx, length(xxx), 1)''' &nbsp; &nbsp; with &nbsp; &nbsp; '''right(xxx,1)'''
:::* &nbsp; removes a useless &nbsp; '''parse''' &nbsp; statement
:::* &nbsp; compresses '''63''' lines to '''29''' lines
:::* &nbsp; reordered &nbsp; '''if''' &nbsp; statements by most likely to occur
This REXX version &nbsp; '''won't''' &nbsp; handle:
:::* &nbsp; Roman numbers like &nbsp; '''IIXX'''
:::* &nbsp; the &nbsp; '''j''' &nbsp; and &nbsp; '''u''' &nbsp; numerals
:::* &nbsp; (deep) parenthesis type Roman numbers
<syntaxhighlight lang="rexx">/*REXX program converts Roman numeral number(s) ───► Arabic numerals (or numbers). */
rYear = 'MCMXC' ; say right(rYear, 9)":" rom2dec(rYear)
rYear = 'mmviii' ; say right(rYear, 9)":" rom2dec(rYear)
rYear = 'MDCLXVI' ; say right(rYear, 9)":" rom2dec(rYear)
exit /*stick a fork in it, we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
rom2dec: procedure; arg roman . /*obtain the Roman numeral number. */
if verify(roman, 'MDCLXVI')\==0 then return "***error*** invalid Roman number:" roman
#=rChar(right(roman, 1)) /*start with the last Roman numeral. */
do j=1 for length(roman) - 1
x=rChar( substr(roman, j , 1) ) /*extract the current Roman numeral. */
y=rChar( substr(roman, j+1, 1) ) /*extract the next Roman numeral. */
if x<y then # = #-x /*Is x<y ? Then subtract it. */
else # = #+x /*Is x≥y ? " add " */
end /*j*/
return #
/*──────────────────────────────────────────────────────────────────────────────────────*/
rChar: procedure; arg _ /*convert Roman number to Arabic digits*/
if _=='I' then return 1
if _=='V' then return 5
if _=='X' then return 10
if _=='L' then return 50
if _=='C' then return 100
if _=='D' then return 500
if _=='M' then return 1000
return 0 /*indicate an invalid Roman numeral. */</syntaxhighlight>

===version 3===
This REXX version allows the use of &nbsp; '''j''' &nbsp; which was being used in the later part of the Holy Roman Empire
<br>(as a trailing &nbsp; ''' i ''' &nbsp; in Roman numerals).

Also, this program converts &nbsp; ''' IIXX ''' &nbsp; correctly. &nbsp; &nbsp; (Note: &nbsp; this Roman numeral was actually chiseled on
<br>some Roman monuments, archways, and tombs/crypts.)

Also supported are larger numbers such as &nbsp; '''(M)''' &nbsp; which is a Roman numeral(s) within a set of grouping
<br>symbols, &nbsp; in this case, a set of parenthesis &nbsp; (brackets and/or braces can also be used).

Deep parentheses are also supported: &nbsp; '''(MM)''' &nbsp; is two million, &nbsp; '''((MMM))''' &nbsp; is three billion.

Normally, the Romans used an overbar (vinculum) for larger numbers (such as &nbsp; '''{{overline|XL}}''' &nbsp; for forty-thousand),
<br>but the use of such a character is very problematic for computers to deal with, &nbsp; so parenthesis are used
<br>instead.

The Romans also had symbols for some fractions which would be a good addition to this task.
<br>Also, lowercase &nbsp; '''u''' &nbsp; was also used for lowercase &nbsp; '''v'''
<br>Also note that &nbsp; '''IIII''' &nbsp; is a legal Roman numeral construct; &nbsp; (as demonstrated by almost any old clock or
<br>"dialed" wristwatch that has Roman numerals).
<syntaxhighlight lang="rexx">/*REXX program converts Roman numeral number(s) ───► Arabic numerals (or numbers). */
numeric digits 1000 /*so we can handle the big numbers. */
parse arg z /*obtain optional arguments from the CL*/
if z='' then z= "MCMXC mmviii IIXX LU MDCLXVI MDWLXVI ((mmm)) [[[[[D]]]]]" /*defaults.*/

do j=1 for words(z); y=word(z, j) /*process each of the Roman numbers. */
say right(y, 20)':' rom2dec(y) /*display original and decimal version.*/
end /*j*/
exit /*stick a fork in it, we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
rom2dec: procedure; h='0'x; #=0; $=1; arg n . /*"ARG" uppercases N. */
n=translate(n, '()()', "[]{}"); _=verify(n, 'MDCLXVUIJ()') /*trans grouping symbols.*/
if _\==0 then return '***error*** invalid Roman numeral:' substr(n,_,1) /*tell error*/
@.=1; @.m=1000; @.d=500; @.c=100; @.l=50; @.x=10; @.u=5; @.v=5 /*Roman numeral values. */
/* [↓] convert number. */
do k=length(n) to 1 by -1; _=substr(n, k, 1) /*examine a Roman numeral*/
/* [↑] scale up or down.*/
if _=='(' | _==")" then do; $=$*1000; if _=='(' then $=1 /* (≡scale ↑; )≡scale ↓ */
iterate /*go & process next digit*/
end
_=@._*$ /*scale it if necessary. */
if _>h then h=_ /*remember Roman numeral.*/
if _<h then #=#-_ /*char>next? Then sub. */
else #=#+_ /* else add. */
end /*k*/
return # /*return Arabic number. */</syntaxhighlight>
'''output''' &nbsp; when using the default inputs:
<pre>
MCMXC: 1990
mmviii: 2008
IIXX: 18
LU: 55
MDCLXVI: 1666
MDWLXVI: ***error*** invalid Roman numeral: W
((mmm)): 3000000000
[[[[[D]]]]]: 500000000000000000
</pre>

=={{header|Ring}}==
<syntaxhighlight lang="ring">
symbols = "MDCLXVI"
weights = [1000,500,100,50,10,5,1]

see "MCMXCIX = " + romanDec("MCMXCIX") + nl
see "MDCLXVI =" + romanDec("MDCLXVI") + nl
see "XXV = " + romanDec("XXV") + nl
see "CMLIV = " + romanDec("CMLIV") + nl
see "MMXI = " + romanDec("MMXI") + nl

func romanDec roman
n = 0
lastval = 0
arabic = 0
for i = len(roman) to 1 step -1
n = substr(symbols,roman[i])
if n > 0 n = weights[n] ok
if n < lastval arabic = arabic - n
else arabic = arabic + n ok
lastval = n
next
return arabic
</syntaxhighlight>

=={{header|RPL}}==
{{works with|Halcyon Calc|4.2.7}}
{| class="wikitable"
! RPL code
! Comment
|-
|
≪ DUP SIZE "IVXLCDM" { 1 5 10 50 100 500 1000 }
→ rom siz dig val
≪ 0 1 siz '''FOR''' j
rom j DUP SUB
'''IF''' dig SWAP POS '''THEN''' val LAST GET '''END'''
'''IF''' DUP2 < '''THEN''' SWAP NEG SWAP '''END'''
'''NEXT'''
0 1 siz '''START''' + '''NEXT''' +
≫ ≫ ''''ROM→'''' STO
|
'''ROM→''' ''( "ROMAN" -- n )''
store input string, length and tables
scan string from highest digit
get jth character
if char in the table then push its value into stack
if > to previous value then change sign of previous value
sum the stack
.
|}


=={{header|Ruby}}==
=={{header|Ruby}}==
<lang ruby>def fromRoman(roman)
<syntaxhighlight lang="ruby">def fromRoman(roman)
r = roman.dup.upcase
r = roman.upcase
n = 0
n = 0
until r.empty? do
until r.empty? do
Line 1,081: Line 6,560:
end
end
n += v
n += v
r[0 .. len-1] = ""
r.slice!(0,len)
end
end
n
n
end
end


[ "MCMXC", "MMVIII", "MDCLXVI" ].each {|r| pp [r, fromRoman(r)]}</lang>
[ "MCMXC", "MMVIII", "MDCLXVI" ].each {|r| p r => fromRoman(r)}</syntaxhighlight>

outputs
{{out}}
<pre>["MCMXC", 1990]
<pre>
["MMVIII", 2008]
{"MCMXC"=>1990}
["MDCLXVI", 1666]</pre>
{"MMVIII"=>2008}
{"MDCLXVI"=>1666}
</pre>
or
<syntaxhighlight lang="ruby">SYMBOLS = [ ['M', 1000], ['CM', 900], ['D', 500], ['CD', 400], ['C', 100], ['XC', 90],
['L', 50], ['XL', 40], ['X', 10], ['IX', 9], ['V', 5], ['IV', 4], ['I', 1] ]

def parseRoman(roman)
r = roman.upcase
n = 0
SYMBOLS.each { |sym, val| n += val while r.sub!(/^#{sym}/, "") }
n
end

[ "MCMXC", "MMVIII", "MDCLXVI" ].each {|r| puts "%8s :%5d" % [r, parseRoman(r)]}</syntaxhighlight>

{{out}}
<pre>
MCMXC : 1990
MMVIII : 2008
MDCLXVI : 1666
</pre>

=={{header|Rust}}==
<syntaxhighlight lang="rust">struct RomanNumeral {
symbol: &'static str,
value: u32
}

const NUMERALS: [RomanNumeral; 13] = [
RomanNumeral {symbol: "M", value: 1000},
RomanNumeral {symbol: "CM", value: 900},
RomanNumeral {symbol: "D", value: 500},
RomanNumeral {symbol: "CD", value: 400},
RomanNumeral {symbol: "C", value: 100},
RomanNumeral {symbol: "XC", value: 90},
RomanNumeral {symbol: "L", value: 50},
RomanNumeral {symbol: "XL", value: 40},
RomanNumeral {symbol: "X", value: 10},
RomanNumeral {symbol: "IX", value: 9},
RomanNumeral {symbol: "V", value: 5},
RomanNumeral {symbol: "IV", value: 4},
RomanNumeral {symbol: "I", value: 1}
];

fn to_hindu(roman: &str) -> u32 {
match NUMERALS.iter().find(|num| roman.starts_with(num.symbol)) {
Some(num) => num.value + to_hindu(&roman[num.symbol.len()..]),
None => 0, // if string empty, add nothing
}
}

fn main() {
let roms = ["MMXIV", "MCMXCIX", "XXV", "MDCLXVI", "MMMDCCCLXXXVIII"];
for &r in &roms {
// 15 is minimum formatting width of the first argument, there for alignment
println!("{:2$} = {}", r, to_hindu(r), 15);
}
}</syntaxhighlight>
{{out}}
<pre>MMXIV = 2014
MCMXCIX = 1999
XXV = 25
MDCLXVI = 1666
MMMDCCCLXXXVIII = 3888</pre>

=={{header|Scala}}==
<syntaxhighlight lang="scala">def fromRoman( r:String ) : Int = {
val arabicNumerals = List("CM"->900,"M"->1000,"CD"->400,"D"->500,"XC"->90,"C"->100,
"XL"->40,"L"->50,"IX"->9,"X"->10,"IV"->4,"V"->5,"I"->1)
var s = r
arabicNumerals.foldLeft(0){ (n,t) => {
val l = s.length; s = s.replaceAll(t._1,""); val c = (l - s.length)/t._1.length // Get the frequency
n + (c*t._2) // Add the arabic numerals up
} }
}

// Here is a another version that does a simple running sum:
def fromRoman2(s: String) : Int = {
val numerals = Map('I' -> 1, 'V' -> 5, 'X' -> 10, 'L' -> 50, 'C' -> 100, 'D' -> 500, 'M' -> 1000)

s.toUpperCase.map(numerals).foldLeft((0,0)) {
case ((sum, last), curr) => (sum + curr + (if (last < curr) -2*last else 0), curr) }._1
}
}

// A small test
def test( roman:String ) = println( roman + " => " + fromRoman( roman ) )
test("MCMXC")
test("MMVIII")
test("MDCLXVI")</syntaxhighlight>
{{out}}
<pre>MCMXC => 1990
MMVIII => 2008
MDCLXVI => 1666</pre>

=={{header|Scheme}}==

{{works with|Gauche Scheme}}

<syntaxhighlight lang="scheme">(use gauche.collection) ;; for fold2

(define (char-val char)
(define i (string-scan "IVXLCDM" char))
(* (expt 10 (div i 2)) (expt 5 (mod i 2))))

(define (decode roman)
(fold2
(lambda (n sum prev-val)
(values ((if (< n prev-val) - +) sum n) (max n prev-val)))
0 0
(map char-val (reverse (string->list roman)))))
</syntaxhighlight>

<b>Testing:</b>
<syntaxhighlight lang="scheme">(for-each
(^s (format #t "~7d: ~d\n" s (decode s)))
'("MCMLVI" "XXC" "MCMXC" "XXCIII" "IIIIX" "MIM" "LXXIIX"))
</syntaxhighlight>
{{out}}
<pre>
MCMLVI: 1956
XXC: 80
MCMXC: 1990
XXCIII: 83
IIIIX: 6
MIM: 1999
LXXIIX: 78
</pre>


=={{header|Seed7}}==
=={{header|Seed7}}==
<lang seed7>$ include "seed7_05.s7i";
<syntaxhighlight lang="seed7">$ include "seed7_05.s7i";


const func integer: ROMAN parse (in string: roman) is func
const func integer: ROMAN parse (in string: roman) is func
Line 1,128: Line 6,738:
writeln(ROMAN parse "MMVIII");
writeln(ROMAN parse "MMVIII");
writeln(ROMAN parse "MDCLXVI");
writeln(ROMAN parse "MDCLXVI");
end func;</lang>
end func;</syntaxhighlight>

Original source: [http://seed7.sourceforge.net/algorith/puzzles.htm#decode_roman_numerals]
Original source: [http://seed7.sourceforge.net/algorith/puzzles.htm#decode_roman_numerals]
{{out}}
<pre>
1990
2008
1666
</pre>


=={{header|SenseTalk}}==
Output:
<syntaxhighlight lang="sensetalk">function RomanNumeralsDecode numerals
put {
"M": 1000,
"D": 500,
"C": 100,
"L": 50,
"X": 10,
"V": 5,
"I": 1
} into values
put 0 into total
repeat with each character letter of numerals
if values.(character the counter + 1 of numerals) is less than or equal to values.(letter)
add values.(letter) to total
else
subtract values.(letter) from total
end if
end repeat
return total
end RomanNumeralsDecode</syntaxhighlight>

<syntaxhighlight lang="sensetalk">repeat for each item in [
"MCMXC",
"MMVIII",
"MDCLXVI",
]
put RomanNumeralsDecode(it)
end repeat
</syntaxhighlight>

{{out}}
<pre>
<pre>
1990
1990
Line 1,139: Line 6,786:
</pre>
</pre>


=={{header|SNOBOL4}}==
=={{header|Sidef}}==
<syntaxhighlight lang="ruby">func roman2arabic(roman) {
 
var arabic = 0
var last_digit = 1000
 
static m = Hash(
I => 1,
V => 5,
X => 10,
L => 50,
C => 100,
D => 500,
M => 1000,
)
 
roman.uc.chars.map{m{_} \\ 0}.each { |digit|
if (last_digit < digit) {
arabic -= (2 * last_digit)
}
arabic += (last_digit = digit)
}
 
return arabic
}
 
%w(MCMXC MMVIII MDCLXVI).each { |roman_digit|
"%-10s == %d\n".printf(roman_digit, roman2arabic(roman_digit))
}</syntaxhighlight>
{{out}}
<pre>
MCMXC == 1990
MMVIII == 2008
MDCLXVI == 1666
</pre>


Simpler solution:
<lang SNOBOL4>* Roman to Arabic
<syntaxhighlight lang="ruby">func roman2arabic(digit) {
digit.uc.trans([
:M: '1000+',
:CM: '900+',
:D: '500+',
:CD: '400+',
:C: '100+',
:XC: '90+',
:L: '50+',
:XL: '40+',
:X: '10+',
:IX: '9+',
:V: '5+',
:IV: '4+',
:I: '1+',
]).split('+').map{.to_i}.sum;
}
 
%w(MCMXC MMVIII MDCLXVI).each { |roman_num|
say "#{roman_num}\t-> #{roman2arabic(roman_num)}";
}</syntaxhighlight>
{{out}}
<pre>
MCMXC -> 1990
MMVIII -> 2008
MDCLXVI -> 1666
</pre>

=={{header|Simula}}==
<syntaxhighlight lang="simula">BEGIN

INTEGER PROCEDURE FROMROMAN(S); TEXT S;
BEGIN
PROCEDURE P(INTVAL, NUM); INTEGER INTVAL; TEXT NUM;
BEGIN
INTEGER NLEN;
NLEN := NUM.LENGTH;
WHILE INDEX + NLEN - 1 <= SLEN AND THEN
S.SUB(INDEX, NLEN) = NUM DO
BEGIN
RESULT := RESULT + INTVAL;
INDEX := INDEX + NLEN;
END WHILE;
END P;
INTEGER RESULT, INDEX, SLEN;
SLEN := S.LENGTH;
INDEX := 1;
P( 1000, "M" );
P( 900, "CM" );
P( 500, "D" );
P( 400, "CD" );
P( 100, "C" );
P( 90, "XC" );
P( 50, "L" );
P( 40, "XL" );
P( 10, "X" );
P( 9, "IX" );
P( 5, "V" );
P( 4, "IV" );
P( 1, "I" );
FROMROMAN := RESULT;
END FROMROMAN;

TEXT T;
FOR T :- "MCMXC", "MMVIII", "MDCLXVI" DO
BEGIN
OUTTEXT("ROMAN """);
OUTTEXT(T);
OUTTEXT(""" => ");
OUTINT(FROMROMAN(T), 0);
OUTIMAGE;
END FOR;

END PROGRAM;
</syntaxhighlight>
{{out}}
<pre>
ROMAN "MCMXC" => 1990
ROMAN "MMVIII" => 2008
ROMAN "MDCLXVI" => 1666
</pre>

=={{header|SNOBOL4}}==
<syntaxhighlight lang="snobol4">* Roman to Arabic
define('arabic(n)s,ch,val,sum,x') :(arabic_end)
define('arabic(n)s,ch,val,sum,x') :(arabic_end)
arabic s = 'M1000 D500 C100 L50 X10 V5 I1 '
arabic s = 'M1000 D500 C100 L50 X10 V5 I1 '
Line 1,157: Line 6,922:
astr = astr r '=' arabic(r) ' ' :(tloop)
astr = astr r '=' arabic(r) ' ' :(tloop)
out output = astr
out output = astr
end</lang>
end</syntaxhighlight>
{{out}}

Output:
<pre>MMX=2010 MCMXCIX=1999 MCDXCII=1492 MLXVI=1066 CDLXXVI=476</pre>
<pre>MMX=2010 MCMXCIX=1999 MCDXCII=1492 MLXVI=1066 CDLXXVI=476</pre>

Here's an alternative version, which is maybe more SNOBOL4-idiomatic and less like one might program it in a more common language:
Here's an alternative version, which is maybe more SNOBOL4-idiomatic and less like one might program it in a more common language:
<syntaxhighlight lang="snobol4">* Roman to Arabic

<lang SNOBOL4>* Roman to Arabic
define("arabic1(romans,arabic1)rdigit,adigit,b4")
define("arabic1(romans,arabic1)rdigit,adigit,b4")
romans1 = " 0 IX9 IV4 III3 II2 I1 VIII8 VII7 VI6 V5" :(arabic1_end)
romans1 = " 0 IX9 IV4 III3 II2 I1 VIII8 VII7 VI6 V5" :(arabic1_end)
Line 1,178: Line 6,940:
astr = astr r '=' arabic1(r) ' ' :(tloop)
astr = astr r '=' arabic1(r) ' ' :(tloop)
out output = astr
out output = astr
end</lang>
end</syntaxhighlight>

The output is the same as in the earlier version.
The output is the same as in the earlier version.


The following version takes advantage of some of the so-called "SPITBOL extensions", which are to be found in most modern implementations. This allows removing several labels and explicit transfers of control, and moves some of the looping into the pattern matcher. Again, the output is the same.
The following version takes advantage of some of the so-called "SPITBOL extensions", which are to be found in most modern implementations.
This allows removing several labels and explicit transfers of control, and moves some of the looping into the pattern matcher.

Again, the output is the same.
<lang SNOBOL4>* Roman to Arabic
<syntaxhighlight lang="snobol4">* Roman to Arabic
define("arabic1(romans,arabic1)rdigit,adigit,b4")
define("arabic1(romans,arabic1)rdigit,adigit,b4")
romans1 = " 0 IX9 IV4 III3 II2 I1 VIII8 VII7 VI6 V5" :(arabic1_end)
romans1 = " 0 IX9 IV4 III3 II2 I1 VIII8 VII7 VI6 V5" :(arabic1_end)
Line 1,196: Line 6,958:
tstr span(' ') break(' ') $ r *?(astr = astr r '=' arabic1(r) ' ') fail
tstr span(' ') break(' ') $ r *?(astr = astr r '=' arabic1(r) ' ') fail
output = astr
output = astr
end</lang>
end</syntaxhighlight>

=={{header|SPL}}==
<syntaxhighlight lang="spl">r2a(r)=
n = [1,5,10,50,100,500,1000]
a,m = 0
> i, #.size(r)..1, -1
v,c = n[#.pos("IVXLCDM",#.mid(r,i))]
? v<m, v = -v
? c>m, m = c
a += v
<
<= a
.

t = ["MMXI","MIM","MCMLVI","MDCLXVI","XXCIII","LXXIIX","IIIIX"]
> i, 1..#.size(t,1)
#.output(t[i]," = ",r2a(t[i]))
<</syntaxhighlight>
{{out}}
<pre>
MMXI = 2011
MIM = 1999
MCMLVI = 1956
MDCLXVI = 1666
XXCIII = 83
LXXIIX = 78
IIIIX = 6
</pre>

=={{header|Swift}}==
<syntaxhighlight lang="swift">extension Int {
init(romanNumerals: String) {
let values = [
( "M", 1000),
("CM", 900),
( "D", 500),
("CD", 400),
( "C", 100),
("XC", 90),
( "L", 50),
("XL", 40),
( "X", 10),
("IX", 9),
( "V", 5),
("IV", 4),
( "I", 1),
]
self = 0
var raw = romanNumerals
for (digit, value) in values {
while raw.hasPrefix(digit) {
self += value
raw.removeFirst(digit.count)
}
}
}
}
</syntaxhighlight>
{{output}}
<syntaxhighlight lang="swift">Int(romanNumerals: "MDCLXVI") // 1666</syntaxhighlight>

=={{header|Tailspin}}==
<syntaxhighlight lang="tailspin">
def digits: [(M:1000"1"), (CM:900"1"), (D:500"1"), (CD:400"1"), (C:100"1"), (XC:90"1"), (L:50"1"), (XL:40"1"), (X:10"1"), (IX:9"1"), (V:5"1"), (IV:4"1"), (I:1"1")];
composer decodeRoman
@: 1;
[ <digit>* ] -> \(@: 0"1"; $... -> @: $@ + $; $@ !\)
rule digit: <value>* (@: $@ + 1;)
rule value: <='$digits($@)::key;'> -> $digits($@)::value
end decodeRoman

'MCMXC' -> decodeRoman -> !OUT::write
'
' -> !OUT::write
'MMVIII' -> decodeRoman -> !OUT::write
'
' -> !OUT::write
'MDCLXVI' -> decodeRoman -> !OUT::write
</syntaxhighlight>
{{out}}
<pre>
1990"1"
2008"1"
1666"1"
</pre>


=={{header|Tcl}}==
=={{header|Tcl}}==
As long as we assume that we have a valid roman number, this is most easily done by transforming the number into a sum and evaluating the expression:
As long as we assume that we have a valid roman number, this is most easily done by transforming the number into a sum and evaluating the expression:
<lang tcl>proc fromRoman rnum {
<syntaxhighlight lang="tcl">proc fromRoman rnum {
set map {M 1000+ CM 900+ D 500+ CD 400+ C 100+ XC 90+ L 50+ XL 40+ X 10+ IX 9+ V 5+ IV 4+ I 1+}
set map {M 1000+ CM 900+ D 500+ CD 400+ C 100+ XC 90+ L 50+ XL 40+ X 10+ IX 9+ V 5+ IV 4+ I 1+}
expr [string map $map $rnum]0}
expr [string map $map $rnum]0}
}</lang>
}</syntaxhighlight>
Demonstrating:
Demonstrating:
<lang tcl>foreach r {MCMXC MDCLXVI MMVIII} {
<syntaxhighlight lang="tcl">foreach r {MCMXC MDCLXVI MMVIII} {
puts "$r\t-> [fromRoman $r]"
puts "$r\t-> [fromRoman $r]"
}</lang>
}</syntaxhighlight>
{{out}}
Output:
<pre>MCMXC -> 1990
<pre>MCMXC -> 1990
MDCLXVI -> 1666
MDCLXVI -> 1666
MMVIII -> 2008</pre>
MMVIII -> 2008</pre>

=={{header|TMG}}==
Unix TMG dialect. Version without validation:
<syntaxhighlight lang="unixtmg">loop: parse(roman)\loop;
roman: string(!<<MDCLXVI>>) [n=0] num
letter: num/render letter;
num: <M> [n=+1750]
| <D> [n=+764]
| <C> ( <M> [n=+1604]
| <D> [n=+620]
| [n=+144] )
| <L> [n=+62]
| <X> ( <C> [n=+132]
| <L> [n=+50]
| [n=+12] )
| <V> [n=+5]
| <I> ( <X> [n=+11]
| <V> [n=+4]
| [n++] );
render: decimal(n) = { 1 * };

n: 0;</syntaxhighlight>

Unix TMG dialect. Version with validation:
<syntaxhighlight lang="unixtmg">loop: [wsz = &a - &n]
parse(line)\loop
parse(error)\loop;
line: roman *;
roman: [n=0] [off=0]
comb((<^>),(<&>),(<M>))
comb((<M>),(<D>),(<C>))
comb((<C>),(<L>),(<X>))
comb((<X>),(<V>),(<I>))
[n>0?] decimal(n) = { 1 * };
comb: proc(c1,c2,c3)
[v1 = *(wsz*off++ + &a)]
[v2 = *(wsz*off++ + &a)]
[v3 = *(wsz*off + &a)]
( c3 ( c3 ( c3 [n=+(3*v3)]
| [n=+(2*v3)] )
| c1 [v1>0?] [n=+v1-v3]
| c2 [v2>0?] [n=+v2-v3]
| [n=+v3] )
| c2 [v2>0?] [n=+v2]
( c3 ( c3 ( c3 [n=+(3*v3)]
| [n=+(2*v3)] )
| [n=+v3] )
| () )
| () );
error: smark ignore(<<>>) string(notnewline) scopy *
= { <error: > 1 * };

notnewline: !<<
>>;

n: 0;
a: 0; 0; 1750; 764; 144; 62; 12; 5; 1;
off:0;
wsz:0;
v1: 0; v2: 0; v3: 0;</syntaxhighlight>

Sample input:
<pre>MMXVI
LV
XII
MCMLIV
IIXX
IM
XXCIII</pre>

Sample output:
<pre>2016
55
12
1954
error: IIXX
error: IM
error: XXCIII</pre>


=={{header|TUSCRIPT}}==
=={{header|TUSCRIPT}}==
<lang tuscript>
<syntaxhighlight lang="tuscript">$$ MODE TUSCRIPT
$$ MODE TUSCRIPT
LOOP roman_number="MCMXC'MMVIII'MDCLXVI"
LOOP roman_number="MCMXC'MMVIII'MDCLXVI"
arab_number=DECODE (roman_number,ROMAN)
arab_number=DECODE (roman_number,ROMAN)
PRINT "Roman number ",roman_number," equals ", arab_number
PRINT "Roman number ",roman_number," equals ", arab_number
ENDLOOP
ENDLOOP</syntaxhighlight>
{{out}}
</lang>
Output:
<pre>
<pre>
Roman number MCMXC equals 1990
Roman number MCMXC equals 1990
Line 1,228: Line 7,152:
</pre>
</pre>


=={{header|Vedit macro language}}==
=={{header|UNIX Shell}}==
<syntaxhighlight lang="bash">
#!/bin/bash


roman_to_dec() {
<lang vedit>// Main program for testing the function
local rnum=$1
local n=0
local prev=0

for ((i=${#rnum}-1;i>=0;i--))
do
case "${rnum:$i:1}" in
M) a=1000 ;;
D) a=500 ;;
C) a=100 ;;
L) a=50 ;;
X) a=10 ;;
V) a=5 ;;
I) a=1 ;;
esac

if [[ $a -lt $prev ]]
then
let n-=a
else
let n+=a
fi

prev=$a
done

echo "$rnum = $n"
}

roman_to_dec MCMXC
roman_to_dec MMVIII
roman_to_dec MDCLXVI
</syntaxhighlight>

=={{header|VBA}}==
Convert Romans (i.e : XVI) in integers
<syntaxhighlight lang="vb">
Option Explicit

Sub Main_Romans_Decode()
Dim Arr(), i&

Arr = Array("III", "XXX", "CCC", "MMM", "VII", "LXVI", "CL", "MCC", "IV", "IX", "XC", "ICM", "DCCCXCIX", "CMI", "CIM", "MDCLXVI", "MCMXC", "MMXVII")
For i = 0 To UBound(Arr)
Debug.Print Arr(i) & " >>> " & lngConvert(CStr(Arr(i)))
Next
End Sub

Function Convert(Letter As String) As Long
Dim Romans(), DecInt(), Pos As Integer

Romans = Array("M", "D", "C", "L", "X", "V", "I")
DecInt = Array(1000, 500, 100, 50, 10, 5, 1)
Pos = -1
On Error Resume Next
Pos = Application.Match(Letter, Romans, 0) - 1
On Error GoTo 0
If Pos <> -1 Then Convert = DecInt(Pos)
End Function

Function lngConvert(strRom As String) 'recursive function
Dim i As Long, iVal As Integer

If Len(strRom) = 1 Then
lngConvert = Convert(strRom)
Else
iVal = Convert(Mid(strRom, 1, 1))
If iVal < Convert(Mid(strRom, 2, 1)) Then iVal = iVal * (-1)
lngConvert = iVal + lngConvert(Mid(strRom, 2, Len(strRom) - 1))
End If
End Function
</syntaxhighlight>
{{out}}
<pre>III >>> 3
XXX >>> 30
CCC >>> 300
MMM >>> 3000
VII >>> 7
LXVI >>> 66
CL >>> 150
MCC >>> 1200
IV >>> 4
IX >>> 9
XC >>> 90
ICM >>> 899
DCCCXCIX >>> 899
CMI >>> 901
CIM >>> 1099
MDCLXVI >>> 1666
MCMXC >>> 1990
MMXVII >>> 2017</pre>

=={{header|VBScript}}==
{{trans|360 Assembly}}
<syntaxhighlight lang="vb">' Roman numerals Encode - Visual Basic - 18/04/2019

Function toRoman(ByVal value)
Dim arabic
Dim roman
Dim i, result
arabic = Array(1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1)
roman = Array("M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I")
For i = 0 To 12
Do While value >= arabic(i)
result = result + roman(i)
value = value - arabic(i)
Loop
Next 'i
toRoman = result
End Function 'toRoman
n=InputBox("Number, please","Roman numerals/Encode")
code=MsgBox(n & vbCrlf & toRoman(n),vbOKOnly+vbExclamation,"Roman numerals/Encode")
If code=vbOK Then ok=1
</syntaxhighlight>
{{out}}
<pre>
III >>> 3
XXX >>> 30
CCC >>> 300
MMM >>> 3000
VII >>> 7
LXVI >>> 66
CL >>> 150
MCC >>> 1200
IV >>> 4
IX >>> 9
XC >>> 90
ICM >>> 901
DCCCXCIX >>> 899
CMI >>> 901
CIM >>> 1099
MDCLXVI >>> 1666
MCMXC >>> 1990
MMXVII >>> 2017
I >>> 1
XIV >>> 14
MMMDCCCLXXXVIII >>> 3888
MMMCMXCIX >>> 3999
</pre>

=={{header|Vedit macro language}}==
<syntaxhighlight lang="vedit">// Main program for testing the function
//
//
do {
do {
Line 1,267: Line 7,336:
Reg_Empty(11)
Reg_Empty(11)
Buf_Quit(OK)
Buf_Quit(OK)
Return</lang>
Return</syntaxhighlight>
{{out}}

Sample output:
<pre>iv = 4
<pre>iv = 4
xii = 12
xii = 12
Line 1,276: Line 7,344:
MMXI = 2011</pre>
MMXI = 2011</pre>


=={{header|Zsh}}==
=={{header|V (Vlang)}}==
{{trans|Kotlin}}
<lang zsh>function parseroman () {
<syntaxhighlight lang="Zig">
const romans = ["I", "III", "IV", "VIII", "XLIX", "CCII", "CDXXXIII", "MCMXC", "MMVIII", "MDCLXVI"]

fn main() {
for roman in romans {println("${roman:-10} = ${roman_decode(roman)}")}
}

fn roman_decode(roman string) int {
mut n := 0
mut last := "O"
if roman =="" {return n}
for c in roman {
match c.ascii_str() {
"I" {n++}
"V" {if last == "I" {n += 3} else {n += 5}}
"X" {if last == "I" {n += 8} else {n += 10}}
"L" {if last == "X" {n += 30} else {n += 50}}
"C" {if last == "X" {n += 80} else {n += 100}}
"D" {if last == "C" {n += 300} else {n += 500}}
"M" {if last == "C" {n += 800} else {n += 1000}}
else {last = c.ascii_str()}
}
}
return n
}
</syntaxhighlight>

{{out}}
<pre>
I = 1
III = 3
IV = 4
VIII = 8
XLIX = 49
CCII = 202
CDXXXIII = 433
MCMXC = 1990
MMVIII = 2008
MDCLXVI = 1666
</pre>

=={{header|Wren}}==
{{trans|Kotlin}}
{{libheader|Wren-fmt}}
<syntaxhighlight lang="wren">import "./fmt" for Fmt

var decode = Fn.new { |r|
if (r == "") return 0
var n = 0
var last = "0"
for (c in r) {
var k
if (c == "I") {
k = 1
} else if (c == "V") {
k = (last == "I") ? 3 : 5
} else if (c == "X") {
k = (last == "I") ? 8 : 10
} else if (c == "L") {
k = (last == "X") ? 30 : 50
} else if (c == "C") {
k = (last == "X") ? 80 : 100
} else if (c == "D") {
k = (last == "C") ? 300 : 500
} else if (c == "M") {
k = (last == "C") ? 800 : 1000
}
n = n + k
last = c
}
return n
}

var romans = ["I", "III", "IV", "VIII", "XLIX", "CCII", "CDXXXIII", "MCMXC", "MMVIII", "MDCLXVI"]
for (r in romans) System.print("%(Fmt.s(-10, r)) = %(decode.call(r))")</syntaxhighlight>

{{out}}
<pre>
I = 1
III = 3
IV = 4
VIII = 8
XLIX = 49
CCII = 202
CDXXXIII = 433
MCMXC = 1990
MMVIII = 2008
MDCLXVI = 1666
</pre>

=={{header|XLISP}}==
Uses basic list processing and recursion. Probably not amazingly fast, but quite concise and hopefully clear.
<syntaxhighlight lang="lisp">(defun decode (r)
(define roman '((#\m 1000) (#\d 500) (#\c 100) (#\l 50) (#\x 10) (#\v 5) (#\i 1)))
(defun to-arabic (rn rs a)
(cond
((null rn) a)
((eqv? (car rn) (caar rs)) (to-arabic (cdr rn) roman (if (and (not (eqv? (car rn) (cadr rn))) (< (cadar rs) (to-arabic (cdr rn) roman 0)))
(- a (cadar rs))
(+ a (cadar rs)) ) ) )
(t (to-arabic rn (cdr rs) a)) ) )
(to-arabic (string->list r) roman 0) )</syntaxhighlight>
Test it in a REPL:
<syntaxhighlight lang="lisp">[1] (mapcar decode '("mcmxc" "mmviii" "mdclxvi"))

(1990 2008 1666)</syntaxhighlight>

=={{header|XPL0}}==
<syntaxhighlight lang="xpl0">string 0; \use zero-terminated strings
code CrLf=9, IntOut=11;

func Roman(Str); \Convert Roman numeral string to decimal value
char Str;
int I, Val, Val0, Sum;
[I:= 0; Sum:= 0; Val0:= 5000;
loop [case Str(I) of
^M: Val:= 1000;
^D: Val:= 500;
^C: Val:= 100;
^L: Val:= 50;
^X: Val:= 10;
^V: Val:= 5;
^I: Val:= 1
other return Sum; \zero string terminator
I:= I+1;
Sum:= Sum + Val;
if Val > Val0 then Sum:= Sum - 2*Val0;
Val0:= Val;
];
];

[IntOut(0, Roman("MCMXC")); CrLf(0);
IntOut(0, Roman("MMVIII")); CrLf(0);
IntOut(0, Roman("MDCLXVI")); CrLf(0);
]</syntaxhighlight>

{{out}}
<pre>
1990
2008
1666
</pre>

=={{header|XQuery}}==
<syntaxhighlight lang="xquery">
xquery version "3.1";

declare function local:decode-roman-numeral($roman-numeral as xs:string) {
$roman-numeral
=> upper-case()
=> for-each(
function($roman-numeral-uppercase) {
analyze-string($roman-numeral-uppercase, ".")/fn:match
! map { "M": 1000, "D": 500, "C": 100, "L": 50, "X": 10, "V": 5, "I": 1 }(.)
}
)
=> fold-right([0,0],
function($number as xs:integer, $accumulator as array(*)) {
let $running-total := $accumulator?1
let $previous-number := $accumulator?2
return
if ($number lt $previous-number) then
[ $running-total - $number, $number ]
else
[ $running-total + $number, $number ]
}
)
=> array:get(1)
};

let $roman-numerals :=
map {
"MCMXCIX": 1999,
"MDCLXVI": 1666,
"XXV": 25,
"XIX": 19,
"XI": 11,
"CMLIV": 954,
"MMXI": 2011,
"CD": 400,
"MCMXC": 1990,
"MMVIII": 2008,
"MMIX": 2009,
"MMMDCCCLXXXVIII": 3888
}
return
map:for-each(
$roman-numerals,
function($roman-numeral, $expected-value) {
local:decode-roman-numeral($roman-numeral) eq $expected-value
}
)
</syntaxhighlight>

=={{header|zkl}}==
<syntaxhighlight lang="zkl">var romans = L(
L("M", 1000), L("CM", 900), L("D", 500), L("CD", 400), L("C", 100),
L("XC", 90), L("L", 50), L("XL", 40), L("X", 10), L("IX", 9),
L("V", 5), L("IV", 4), L("I", 1));
fcn toArabic(romanNumber){ // romanNumber needs to be upper case
if (not RegExp("^[CDILMVX]+$").matches(romanNumber))
throw(Exception.ValueError("Not a Roman number: %s".fmt(romanNumber)));
reg value = 0;
foreach R,N in (romans){
while (0 == romanNumber.find(R)){
value += N;
romanNumber = romanNumber[R.len(),*];
}
}
return(value);
}</syntaxhighlight>
<pre>
toArabic("MCMXC") //-->1990
toArabic("MMVIII") //-->2008
toArabic("MDCLXVI") //-->1666
</pre>

{{omit from|GUISS}}

=={{header|Zoea}}==
<syntaxhighlight lang="zoea">
program: roman_decimal
input: 'XIII'
output: 13
</syntaxhighlight>

=={{header|Zoea Visual}}==
[http://zoea.co.uk/examples/zv-rc/Roman_numerals_decode.png Roman numerals decode]

=={{header|zsh}}==
<syntaxhighlight lang="zsh">
#!/bin/zsh
function parseroman () {
local max=0 sum i j
local max=0 sum i j
local -A conv
local -A conv
Line 1,291: Line 7,592:
done
done
echo $sum
echo $sum
}
}</lang>


parseroman MCMXC
{{omit from|GUISS}}
parseroman MMVIII
parseroman MDCLXVI
</syntaxhighlight>

Latest revision as of 19:14, 12 March 2024

Task
Roman numerals/Decode
You are encouraged to solve this task according to the task description, using any language you may know.
Task

Create a function that takes a Roman numeral as its argument and returns its value as a numeric decimal integer.

You don't need to validate the form of the Roman numeral.

Modern Roman numerals are written by expressing each decimal digit of the number to be encoded separately,
starting with the leftmost decimal digit and skipping any 0s   (zeroes).

1990 is rendered as   MCMXC     (1000 = M,   900 = CM,   90 = XC)     and
2008 is rendered as   MMVIII       (2000 = MM,   8 = VIII).

The Roman numeral for 1666,   MDCLXVI,   uses each letter in descending order.

11l

Translation of: Python
V roman_values = [(‘I’, 1), (‘IV’, 4), (‘V’, 5), (‘IX’, 9), (‘X’, 10),
                  (‘XL’, 40), (‘L’, 50), (‘XC’, 90), (‘C’, 100),
                  (‘CD’, 400), (‘D’, 500), (‘CM’, 900), (‘M’, 1000)]

F roman_value(=roman)
   V total = 0
   L(symbol, value) reversed(:roman_values)
      L roman.starts_with(symbol)
         total += value
         roman = roman[symbol.len..]
   R total

L(value) [‘MCMXC’, ‘MMVIII’, ‘MDCLXVI’]
   print(value‘ = ’roman_value(value))
Output:
MCMXC = 1990
MMVIII = 2008
MDCLXVI = 1666

360 Assembly

*        Roman numerals Decode -   17/04/2019
ROMADEC  CSECT
         USING  ROMADEC,R13        base register
         B      72(R15)            skip savearea
         DC     17F'0'             savearea
         SAVE   (14,12)            save previous context
         ST     R13,4(R15)         link backward
         ST     R15,8(R13)         link forward
         LR     R13,R15            set addressability
         LA     R6,1               i=1 
       DO WHILE=(C,R6,LE,=A(NV))   do i=1 to hbound(vals)
         LR     R1,R6                i
         SLA    R1,3                 ~
         LA     R4,VALS-L'VALS(R1)   @vals(i)
         MVC    X,0(R4)              x=vals(i)
         SR     R9,R9                prev=0
         ST     R9,Y                 y=0
         LA     R7,L'X               j=1 
       DO WHILE=(C,R7,GE,=A(1))      do j=length(x) to 1 by -1
         LA     R4,X-1                 @x
         AR     R4,R7                  +j
         MVC    C,0(R4)                c=substr(x,j,1)
       IF   CLI,C,NE,C' ' THEN         if c^=' ' then
         SR     R1,R1                  r1=0
         LA     R2,1                   k=1
       DO WHILE=(C,R2,LE,=A(L'ROMAN))   do k=1 to length(roman)
         LA     R3,ROMAN-1               @roman
         AR     R3,R2                    +k
       IF   CLC,0(L'C,R3),EQ,C THEN      if substr(roman,k,1)=c
         LR     R1,R2                      index=k
         B      REINDEX                    leave k
       ENDIF    ,                        endif
         LA     R2,1(R2)                 k=k+1
       ENDDO    ,                      enddo k
REINDEX  EQU    *                      r1=index(roman,c)
         SLA    R1,2                   ~
         L      R8,DECIM-4(R1)         n=decim(index(roman,c))
       IF    CR,R8,LT,R9 THEN          if n<prev then
         LCR    R8,R8                    n=-n
       ENDIF    ,                      endif
         L      R2,Y                     y
         AR     R2,R8                    +n
         ST     R2,Y                     y=y+n
         LR     R9,R8                    prev=n
       ENDIF    ,                      endif
         BCTR   R7,0                   j--
       ENDDO    ,                    enddo j
         MVC    PG(8),X              x
         L      R1,Y                 y
         XDECO  R1,XDEC              edit y
         MVC    PG+12(4),XDEC+8      output y
         XPRNT  PG,L'PG              print buffer
         LA     R6,1(R6)             i++ 
       ENDDO    ,                  enddo i
         L      R13,4(0,R13)       restore previous savearea pointer
         RETURN (14,12),RC=0       restore registers from calling sav
NV       EQU    (X-VALS)/L'VALS
ROMAN    DC     CL7'MDCLXVI'
DECIM    DC     F'1000',F'500',F'100',F'50',F'10',F'5',F'1'
VALS     DC     CL8'XIV',CL8'CMI',CL8'MIC',CL8'MCMXC',CL8'MDCLXVI' 
         DC     CL8'MMVIII',CL8'MMXIX',CL8'MMMCMXCV'
X        DS     CL(L'VALS)
Y        DS     F
C        DS     CL1
PG       DC     CL80'........ -> ....'
XDEC     DS     CL12
         REGEQU
         END    ROMADEC
Output:
XIV      ->   14
CMI      ->  901
MIC      -> 1099
MCMXC    -> 1990
MDCLXVI  -> 1666
MMVIII   -> 2008
MMXIX    -> 2019
MMMCMXCV -> 3995

8080 Assembly

The routine at roman takes the address of a zero-terminated string in BC, and returns the value of the Roman number in that string as a 16-bit integer in HL. The Roman numeral must be in uppercase letters.

		org	100h
		jmp	test
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;; Takes a zero-terminated Roman numeral string in BC
		;; and returns 16-bit integer in HL.
		;; All registers destroyed.
roman:		dcx	b
romanfindend:	inx	b	; load next character
		ldax	b	
		inr	e
		ana	a	; are we there yet
		jnz	romanfindend
		lxi	h,0	; zero HL to hold the total
		push	h	; stack holds the previous roman numeral
romanloop:	dcx	b	; get next roman numeral
		ldax	b	; (work backwards)
		call	romandgt
		jc	romandone	; carry set = not Roman anymore
		xthl		; load previous roman numeral
		call	cmpdehl	; DE < HL? 
		mov	h,d	; in any case, DE is now the previous
		mov	l,e	; Roman numeral
		xthl		; bring back the total
		jnc	romanadd
		mov	a,d	; DE (current) < HL (previous)
		cma		; so this Roman digit must be subtracted
		mov	d,a	; from the total. 
		mov	a,e	; so we negate it before adding it
		cma		; two's complement: -de = (~de)+1
		mov	e,a
		inx	d
romanadd:	dad	d	; add to running total
		jmp	romanloop
romandone:	pop	d	; remove temporary variable from stack
		ret
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;; 16-bit compare DE with HL, set flags
		;; accordingly. A destroyed. 
cmpdehl:	mov	a,d
		cmp	h
		rnz
		mov	a,e
		cmp	l
		ret 
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;; Takes a single Roman 'digit' in A, 
		;; and returns its value in DE (0 if invalid)
		;; All other registers preserved.
romandgt:	push	h	; preserve hl for the caller
		lxi	h,romantab
		mvi	e,7	; e=counter
romandgtl:	cmp	m	; check table entry
		jz	romanfound
		inx	h	; move to next table entry
		inx	h
		inx	h
		dcr	e	; decrease counter
		jnz	romandgtl
		pop	h	; we didn't find it
		stc		; set carry
		ret		; return with DE=0
romanfound:	inx	h	; we did find it
		mov	e,m	; load it into DE
		inx	h
		mov	d,m
		pop	h
		ana	a	; clear carry
		ret
romantab:	db	'I',1,0	; 16-bit little endian values
		db	'V',5,0
		db	'X',10,0
		db	'L',50,0
		db	'C',100,0
		db	'D',0f4h,1
		db	'M',0e8h,3
		;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		;; The following is testing and I/O code.
test:		mvi	c,10	; read string from console
		lxi	d,bufdef
		call	5
		mvi	c,9	; print newline
		lxi	d,nl
		call	5
		lxi	b,buf	; run `roman' on the input string
		call	roman	; the result is now in hl
		lxi	d,-10000	
		call	numout	; print 10000s digit
		lxi	d,-1000		
		call	numout	; print 1000s digit
		lxi	d,-100
		call 	numout	; print 100s digit
		lxi	d,-10
		call 	numout	; print 10s digit
		lxi	d,-1	; ...print 1s digit
numout:		mvi	a,-1	
		push	h	
numloop:	inr	a
		pop	b
		push	h
		dad	d
		jc	numloop
		adi	'0'
		mvi	c,2
		mov	e,a
		call	5
		pop	h
		ret
nl:		db	13,10,'$'
bufdef:		db	16,0
buf:		ds	17

Action!

CARD FUNC DecodeRomanDigit(CHAR c)
  IF c='I THEN RETURN (1)
  ELSEIF c='V THEN RETURN (5)
  ELSEIF c='X THEN RETURN (10)
  ELSEIF c='L THEN RETURN (50)
  ELSEIF c='C THEN RETURN (100)
  ELSEIF c='D THEN RETURN (500)
  ELSEIF c='M THEN RETURN (1000)
  FI
RETURN (0)

CARD FUNC DecodeRomanNumber(CHAR ARRAY s)
  CARD res,curr,prev
  BYTE i

  res=0 prev=0 i=s(0)
  WHILE i>0
  DO
    curr=DecodeRomanDigit(s(i))
    IF curr<prev THEN
      res==-curr
    ELSE
      res==+curr
    FI
    prev=curr
    i==-1
  OD 
RETURN (res)

PROC Test(CHAR ARRAY s)
  CARD n
  
  n=DecodeRomanNumber(s)
  PrintF("%S=%U%E",s,n)
RETURN

PROC Main()
  Test("MCMXC")
  Test("MMVIII")
  Test("MDCLXVI")
  Test("MMMDCCCLXXXVIII")
  Test("MMMCMXCIX")
RETURN
Output:

Screenshot from Atari 8-bit computer

MCMXC=1990
MMVIII=2008
MDCLXVI=1666
MMMDCCCLXXXVIII=3888
MMMCMXCIX=3999

Ada

Pragma Ada_2012;
Pragma Assertion_Policy( Check );

With
Unchecked_Conversion,
Ada.Text_IO;

Procedure Test_Roman_Numerals is

    -- We create an enumeration of valid characters, note that they are
    -- character-literals, this is so that we can use literal-strings,
    -- and that their size is that of Integer.
    Type Roman_Digits is ('I', 'V', 'X', 'L', 'C', 'D', 'M' )
    with Size => Integer'Size;

    -- We use a representation-clause ensure the proper integral-value
    -- of each individual character.
    For Roman_Digits use
      (
    'I' => 1,
    'V' => 5,
    'X' => 10,
    'L' => 50,
    'C' => 100,
    'D' => 500,
    'M' => 1000
      );

    -- To convert a Roman_Digit to an integer, we now only need to
    -- read its value as an integer.
    Function Convert is new Unchecked_Conversion
      (	Source => Roman_Digits,	Target => Integer );

    -- Romena_Numeral is a string of Roman_Digit.
    Type Roman_Numeral is array (Positive range <>) of Roman_Digits;

    -- The Numeral_List type is used herein only for testing
    -- and verification-data.
    Type Numeral_List is array (Positive range <>) of
      not null access Roman_Numeral;

    -- The Test_Cases subtype ensures that Test_Data and Validation_Data
    -- both contain the same number of elements, and that the indecies
    -- are the same; essentially the same as:
    -- 
    --    pragma Assert( Test_Data'Length = Validation_Data'Length
    --		   AND Test_Data'First = Validation_Data'First);

    subtype Test_Cases is Positive range 1..14;

    Test_Data : constant Numeral_List(Test_Cases):=
      (
       New Roman_Numeral'("III"),	-- 3
       New Roman_Numeral'("XXX"),	-- 30
       New Roman_Numeral'("CCC"),	-- 300
       New Roman_Numeral'("MMM"),	-- 3000

       New Roman_Numeral'("VII"),	-- 7
       New Roman_Numeral'("LXVI"),	-- 66
       New Roman_Numeral'("CL"),	-- 150
       New Roman_Numeral'("MCC"),	-- 1200

       New Roman_Numeral'("IV"),	-- 4
       New Roman_Numeral'("IX"),	-- 9
       New Roman_Numeral'("XC"),	-- 90

       New Roman_Numeral'("ICM"),	-- 901
       New Roman_Numeral'("CIM"),	-- 899
       
       New Roman_Numeral'("MDCLXVI")	-- 1666
      );

    Validation_Data : constant array(Test_Cases) of Natural:=
      (	3, 30, 300, 3000,
	7, 66, 150, 1200,
	4, 9, 90,
	901, 899,
	1666
      );


    -- In Roman numerals, the subtractive form [IV = 4] was used
    -- very infrequently, the most common form was the addidive
    -- form [IV = 6]. (Consider military logistics and squads.)

    -- SUM returns the Number, read in the additive form.
    Function Sum( Number : Roman_Numeral ) return Natural is
    begin
	Return Result : Natural:= 0 do
	    For Item of Number loop
		    Result:= Result + Convert( Item );
	    end loop;
	End Return;
    end Sum;

    -- EVAL returns Number read in the subtractive form.
    Function Eval( Number : Roman_Numeral ) return Natural is
	Current : Roman_Digits:= 'I';
    begin
	Return Result : Natural:= 0 do
	    For Item of Number loop
		if Current < Item then
		    Result:= Convert(Item) - Result;
		    Current:= Item;
		else
		    Result:= Result + Convert(Item);
		end if;
	    end loop;
	End Return;
    end Eval;

    -- Display the given Roman_Numeral via Text_IO.
    Procedure Put( S: Roman_Numeral ) is
    begin
	For Ch of S loop
	    declare
		-- The 'Image attribute returns the character inside
		-- single-quotes; so we select the character itself.
		C : Character renames Roman_Digits'Image(Ch)(2);
	    begin
		Ada.Text_IO.Put( C );
	    end;
	end loop;
    end;

    -- This displays pass/fail dependant on the parameter.
    Function PF ( Value : Boolean ) Return String is
    begin
	Return Result : String(1..4):= ( if Value then"pass"else"fail" );
    End PF;

Begin
    Ada.Text_IO.Put_Line("Starting Test:");

    for Index in Test_Data'Range loop
	declare
	    Item  : Roman_Numeral renames Test_Data(Index).all;
	    Value : constant Natural := Eval(Item);
	begin
	    Put( Item );

	    Ada.Text_IO.Put( ASCII.HT & "= ");
	    Ada.Text_IO.Put( Value'Img );
	    Ada.Text_IO.Put_Line( ASCII.HT & '[' &
			  PF( Value = Validation_Data(Index) )& ']');
	end;
    end loop;

    
    Ada.Text_IO.Put_Line("Testing complete.");
End Test_Roman_Numerals;
Output:
Starting Test:
III	=  3	[pass]
XXX	=  30	[pass]
CCC	=  300	[pass]
MMM	=  3000	[pass]
VII	=  7	[pass]
LXVI	=  66	[pass]
CL	=  150	[pass]
MCC	=  1200	[pass]
IV	=  4	[pass]
IX	=  9	[pass]
XC	=  90	[pass]
ICM	=  901	[pass]
CIM	=  899	[pass]
MDCLXVI	=  1666	[pass]
Testing complete.

ALGOL 68

Works with: ALGOL 68G version Any - tested with release 2.2.0

Note: roman to int will handle multiple subtraction, e.g. IIIIX for 6.

    PROC roman to int = (STRING roman) INT:
    BEGIN
        PROC roman digit value = (CHAR roman digit) INT:
            (roman digit = "M" | 1000 |:
             roman digit = "D" |  500 |:
             roman digit = "C" |  100 |:
             roman digit = "L" |   50 |:
             roman digit = "X" |   10 |:
             roman digit = "V" |    5 |:
             roman digit = "I" |    1);

        INT result := 0, previous value := 0, run := 0;
        
        FOR i FROM LWB roman TO UPB roman
        DO
            INT value = roman digit value(roman[i]);
            IF previous value = value THEN
                run +:= value
            ELSE
                IF previous value < value THEN
                    result -:= run
                ELSE
                    result +:= run
                FI;
                run := previous value := value
            FI
        OD;
        
        result +:= run
    END;

    MODE TEST = STRUCT (STRING input, INT expected output);
    
    [] TEST roman test = (
        ("MMXI",    2011), ("MIM",     1999),
        ("MCMLVI",  1956), ("MDCLXVI", 1666),
        ("XXCIII",    83), ("LXXIIX",    78),
        ("IIIIX",      6)
    );
    
    print(("Test input  Value   Got", newline, "--------------------------", newline));
    FOR i FROM LWB roman test TO UPB roman test
    DO
        INT output = roman to int(input OF roman test[i]);
        printf(($g, n (12 - UPB input OF roman test[i]) x$, input OF roman test[i]));
        printf(($g(5), 1x, g(5), 1x$, expected output OF roman test[i], output));
        printf(($b("ok", "not ok"), 1l$, output = expected output OF roman test[i]))
    OD

ALGOL W

begin
    % decodes a roman numeral into an integer                                %
    % there must be at least one blank after the numeral                     %
    % This takes a lenient view on roman numbers so e.g. IIXX is 18 - see    %
    % the Discussion                                                         %
    integer procedure romanToDecimal ( string(32) value roman ) ;
    begin
        integer   decimal, rPos, currDigit, nextDigit, seqValue;
        string(1) rDigit;

        % the roman number is a sequence of sequences of roman digits        %
        % if the previous sequence is of higher value digits than the next,  %
        %    the higher value is added to the overall value                  %
        % if the previous seequence is of lower value, it is subtracted      %
        % e.g. MCMLXII                                                       %
        %      the sequences are M, C, M, X, II                              %
        %          M is added, C subtracted, M added, X added and II added   %

        % get the value of a sequence of roman digits                        %
        integer procedure getSequence ;
            if rDigit = " " then begin
                % end of the number                                          %
                0
                end
            else begin
                % have another sequence                                      %
                integer sValue;
                sValue := 0;
                while roman( rPos // 1 ) = rDigit do begin
                    sValue := sValue + currDigit;
                    rPos   := rPos + 1;
                end while_have_same_digit ;
                % remember the next digit                                    %
                rDigit     := roman( rPos // 1 );
                % result is the sequence value                               %
                sValue
             end getSequence ;

        % convert a roman digit into its decimal equivalent                  %
        % an invalid digit will terminate the program, " " is 0              %
        integer procedure getValue( string(1) value romanDigit ) ;
            if      romanDigit = "m" or romanDigit = "M" then 1000
            else if romanDigit = "d" or romanDigit = "D" then  500
            else if romanDigit = "c" or romanDigit = "C" then  100
            else if romanDigit = "l" or romanDigit = "L" then   50
            else if romanDigit = "x" or romanDigit = "X" then   10
            else if romanDigit = "v" or romanDigit = "V" then    5
            else if romanDigit = "i" or romanDigit = "I" then    1
            else if romanDigit = " "                     then    0
            else begin
                write( s_w := 0, "Invalid roman digit: """, romanDigit, """" );
                assert false;
                0
            end getValue ;

        % get the first sequence                                             %
        decimal   := 0;
        rPos      := 0;
        rDigit    := roman( rPos // 1 );
        currDigit := getValue( rDigit );
        seqValue  := getSequence;

        % handle the sequences                                               %
        while rDigit not = " " do begin
            % have another sequence                                          %
            nextDigit := getValue( rDigit );
            if currDigit < nextDigit
            then % prev digit is lower  % decimal := decimal - seqValue
            else % prev digit is higher % decimal := decimal + seqValue
            ;
            currDigit := nextDigit;
            seqValue  := getSequence;
        end while_have_a_roman_digit ;

        % add the final sequence                                             %
        decimal + seqValue
    end roman ;

    % test the romanToDecimal routine                                        %

    procedure testRoman ( string(32) value romanNumber ) ;
        write( i_w := 5, romanNumber, romanToDecimal( romanNumber ) );

    testRoman( "I"        );    testRoman( "II"       );
    testRoman( "III"      );    testRoman( "IV"       );
    testRoman( "V"        );    testRoman( "VI"       );
    testRoman( "VII"      );    testRoman( "VIII"     );
    testRoman( "IX"       );    testRoman( "IIXX"     );
    testRoman( "XIX"      );    testRoman( "XX"       );
    write( "..." );
    testRoman( "MCMXC"    );
    testRoman( "MMVIII"   );
    testRoman( "MDCLXVI"  );

end.
Output:
I                                   1
II                                  2
III                                 3
IV                                  4
V                                   5
VI                                  6
VII                                 7
VIII                                8
IX                                  9
IIXX                               18
XIX                                19
XX                                 20
...
MCMXC                            1990
MMVIII                           2008
MDCLXVI                          1666

ANTLR

Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral
Roman Numeral


Java

/* Parse Roman Numerals
   
   Nigel Galloway March 16th., 2012
*/
grammar ParseRN ;

options {
	language = Java;
}
@members {
int rnValue;
int ONE;
}

parseRN:	({rnValue = 0;} rn NEWLINE {System.out.println($rn.text + " = " + rnValue);})*
	;
	
rn	:	(Thousand {rnValue += 1000;})* hundreds? tens? units?;

hundreds:	{ONE = 0;} (h9 | h5) {if (ONE > 3) System.out.println ("Too many hundreds");};
h9	:	Hundred {ONE += 1;} (FiveHund {rnValue += 400;}| Thousand {rnValue += 900;}|{rnValue += 100;} (Hundred {rnValue += 100; ONE += 1;})*);
h5	:	FiveHund {rnValue += 500;} (Hundred {rnValue += 100; ONE += 1;})*;

tens	:	{ONE = 0;} (t9 | t5) {if (ONE > 3) System.out.println ("Too many tens");};
t9	:	Ten {ONE += 1;} (Fifty {rnValue += 40;}| Hundred {rnValue += 90;}|{rnValue += 10;} (Ten {rnValue += 10; ONE += 1;})*);
t5	:	Fifty {rnValue += 50;} (Ten {rnValue += 10; ONE += 1;})*;
	
units	:	{ONE = 0;} (u9 | u5) {if (ONE > 3) System.out.println ("Too many ones");};
u9	:	One {ONE += 1;} (Five {rnValue += 4;}| Ten {rnValue += 9;}|{rnValue += 1;} (One {rnValue += 1; ONE += 1;})*);
u5	:	Five {rnValue += 5;} (One {rnValue += 1; ONE += 1;})*;
	
One	:	'I';
Five	:	'V';
Ten	:	'X';
Fifty	:	'L';
Hundred:	'C';
FiveHund:	'D';
Thousand:	'M' ;
NEWLINE:	'\r'? '\n' ;

Using this test data:

MMXI
MCMLVI
XXCIII
MCMXC
MMVIII
MDCLXVI
IIIIX
MIM
MDCLXVI
LXXIIX
M
MCXI
CMXI
MCM
MMIX
MCDXLIV
MMXII

Produces:

MMXI = 2011
MCMLVI = 1956
line 3:2 missing NEWLINE at 'C'
XX = 20
CIII = 103

Note that this implementation does not accept XXC as eighty. The error is detected and ANTLR attempts to continue by inserting the expected NEWLINE after XX and treating CIII as a new Number.

MCMXC = 1990
MMVIII = 2008
MDCLXVI = 1666
Too many ones
line 7:4 extraneous input 'X' expecting NEWLINE
IIII = 4

An implementation above thinks IIIIX is 6. It isn't. ANTLR detects the surfiet of 'I' reports the errors and tries to carry on.

line 8:2 no viable alternative at input 'M'
MIM = 1000
MDCLXVI = 1666
line 10:5 extraneous input 'X' expecting NEWLINE
LXXII = 72
M = 1000
MCXI = 1111
CMXI = 911
MCM = 1900
MMIX = 2009
MCDXLIV = 1444
MMXII = 2012

APL

Works with: Dyalog APL
fromRoman{
    rmn(⎕A,⎕A,'*')[(⎕A,⎕UCS 96+⍳26)]     ⍝ make input uppercase
    dgt'IVXLCDM' (1 5 10 50 100 500 1000) ⍝ values of roman digits
    ~rmn.∊⊂dgt[1;]:⎕SIGNAL 11              ⍝ domain error if non-roman input
    mapdgt[2;dgt[1;]rmn]                  ⍝ map digits to values
    +/map×1-2×(2</map),0                    ⍝ subtractive principle
}
Output:
      fromRoman¨ 'MCMXC' 'MMVIII' 'MDCLXVI' 'MMXXI'
1990 2008 1666 2021

AppleScript

isPrefixOf

Translation of: JavaScript

(Functional ES5 version)

Translation of: Haskell
------------- INTEGER VALUE OF A ROMAN STRING ------------

-- romanValue :: String -> Int
on romanValue(s)
    script roman
        property mapping : [["M", 1000], ["CM", 900], ["D", 500], ["CD", 400], ¬
            ["C", 100], ["XC", 90], ["L", 50], ["XL", 40], ["X", 10], ["IX", 9], ¬
            ["V", 5], ["IV", 4], ["I", 1]]
        
        -- Value of first Roman glyph + value of remaining glyphs
        -- toArabic :: [Char] -> Int
        on toArabic(xs)
            -- transcribe :: (String, Number) -> Maybe (Number, [String])
            script transcribe
                on |λ|(pair)
                    set {r, v} to pair
                    if isPrefixOf(characters of r, xs) then
                        
                        -- Value of this matching glyph, with any remaining glyphs
                        {v, drop(length of r, xs)}
                    else
                        {}
                    end if
                end |λ|
            end script
            
            if 0 < length of xs then
                set parsed to concatMap(transcribe, mapping)
                (item 1 of parsed) + toArabic(item 2 of parsed)
            else
                0
            end if
        end toArabic
    end script
    
    toArabic(characters of s) of roman
end romanValue

--------------------------- TEST -------------------------
on run
    map(romanValue, {"MCMXC", "MDCLXVI", "MMVIII"})
    
    --> {1990, 1666, 2008}
end run


-------------------- GENERIC FUNCTIONS -------------------

-- concatMap :: (a -> [b]) -> [a] -> [b]
on concatMap(f, xs)
    set lng to length of xs
    set acc to {}
    tell mReturn(f)
        repeat with i from 1 to lng
            set acc to acc & (|λ|(item i of xs, i, xs))
        end repeat
    end tell
    if {text, string} contains class of xs then
        acc as text
    else
        acc
    end if
end concatMap


-- drop :: Int -> [a] -> [a]
-- drop :: Int -> String -> String
on drop(n, xs)
    set c to class of xs
    if script is not c then
        if string is not c then
            if n < length of xs then
                items (1 + n) thru -1 of xs
            else
                {}
            end if
        else
            if n < length of xs then
                text (1 + n) thru -1 of xs
            else
                ""
            end if
        end if
    else
        take(n, xs) -- consumed
        return xs
    end if
end drop


-- isPrefixOf :: [a] -> [a] -> Bool
-- isPrefixOf :: String -> String -> Bool
on isPrefixOf(xs, ys)
    -- isPrefixOf takes two lists or strings and returns 
    --  true if and only if the first is a prefix of the second.
    script go
        on |λ|(xs, ys)
            set intX to length of xs
            if intX < 1 then
                true
            else if intX > length of ys then
                false
            else if class of xs is string then
                (offset of xs in ys) = 1
            else
                set {xxt, yyt} to {uncons(xs), uncons(ys)}
                ((item 1 of xxt) = (item 1 of yyt)) and ¬
                    |λ|(item 2 of xxt, item 2 of yyt)
            end if
        end |λ|
    end script
    go's |λ|(xs, ys)
end isPrefixOf


-- length :: [a] -> Int
on |length|(xs)
    set c to class of xs
    if list is c or string is c then
        length of xs
    else
        (2 ^ 29 - 1) -- (maxInt - simple proxy for non-finite)
    end if
end |length|


-- map :: (a -> b) -> [a] -> [b]
on map(f, xs)
    tell mReturn(f)
        set lng to length of xs
        set lst to {}
        repeat with i from 1 to lng
            set end of lst to |λ|(item i of xs, i, xs)
        end repeat
        return lst
    end tell
end map


-- Lift 2nd class handler function into 1st class script wrapper 
-- mReturn :: Handler -> Script
on mReturn(f)
    if class of f is script then
        f
    else
        script
            property |λ| : f
        end script
    end if
end mReturn


-- uncons :: [a] -> Maybe (a, [a])
on uncons(xs)
    set lng to |length|(xs)
    if 0 = lng then
        missing value
    else
        if (2 ^ 29 - 1) as integer > lng then
            if class of xs is string then
                set cs to text items of xs
                {item 1 of cs, rest of cs}
            else
                {item 1 of xs, rest of xs}
            end if
        else
            set nxt to take(1, xs)
            if {} is nxt then
                missing value
            else
                {item 1 of nxt, xs}
            end if
        end if
    end if
end uncons
Output:
{1990, 1666, 2008}

Fold right – subtracting or adding

Works with: Yosemite onwards
Translation of: Haskell
use framework "Foundation"

----------- INTEGER VALUE OF ROMAN NUMBER STRING ---------

-- fromRoman :: String -> Int
on fromRoman(s)
    script subtractIfLower
        on |λ|(l, rn)
            set {r, n} to rn
            if l  r then -- Digit values that increase (L to R),
                {l, n + l} -- (added)
            else
                {l, n - l} -- Digit values that go down: subtracted.
            end if
        end |λ|
    end script
    
    item 2 of foldr(subtractIfLower, {0, 0}, ¬
        map(my charVal, characters of s))
end fromRoman


-- charVal :: Char -> Int
on charVal(C)
    set v to lookup(toUpper(C), ¬
        {I:1, |V|:5, X:10, |L|:50, C:100, D:500, M:1000})
    if missing value is v then
        0
    else
        v
    end if
end charVal


--------------------------- TEST -------------------------
on run
    map(fromRoman, ¬
        {"MDCLXVI", "MCMXC", "MMVIII", "MMXVI", "MMXXI"})
    
    --> {1666, 1990, 2008, 2016, 2021}
end run


-------------------- GENERIC FUNCTIONS -------------------

-- foldr :: (a -> b -> b) -> b -> [a] -> b
on foldr(f, startValue, xs)
    tell mReturn(f)
        set v to startValue
        set lng to length of xs
        repeat with I from lng to 1 by -1
            set v to |λ|(item I of xs, v)
        end repeat
        return v
    end tell
end foldr


-- lookup :: a -> Dict -> Maybe b
on lookup(k, dct)
    -- Just the value of k in the dictionary,
    -- or missing value if k is not found.
    set ca to current application
    set v to (ca's NSDictionary's dictionaryWithDictionary:dct)'s ¬
        objectForKey:k
    if missing value  v then
        item 1 of ((ca's NSArray's arrayWithObject:v) as list)
    else
        missing value
    end if
end lookup


-- map :: (a -> b) -> [a] -> [b]
on map(f, xs)
    -- The list obtained by applying f
    -- to each element of xs.
    tell mReturn(f)
        set lng to length of xs
        set lst to {}
        repeat with I from 1 to lng
            set end of lst to |λ|(item I of xs, I, xs)
        end repeat
        return lst
    end tell
end map


-- Lift 2nd class handler function into 1st class 
-- script wrapper 
-- mReturn :: Handler -> Script
on mReturn(f)
    if class of f is script then
        f
    else
        script
            property |λ| : f
        end script
    end if
end mReturn


-- toUpper :: String -> String
on toUpper(str)
    tell current application
        ((its (NSString's stringWithString:(str)))'s ¬
            uppercaseStringWithLocale:¬
                (its NSLocale's currentLocale())) as text
    end tell
end toUpper
Output:
{1666, 1990, 2008, 2016, 2021}

BASIC

Applesoft BASIC

Translation of: BBC BASIC
 10  LET R$ = "MCMXCIX"
 20  GOSUB 100 PRINT "ROMAN NUMERALS DECODED"
 30  LET R$ = "MMXII"
 40  GOSUB 100
 50  LET R$ = "MDCLXVI"
 60  GOSUB 100
 70  LET R$ = "MMMDCCCLXXXVIII"
 80  GOSUB 100
 90  END 
 100  PRINT M$R$,
 110  LET M$ =  CHR$ (13)
 120  GOSUB 150"ROMAN NUMERALS DECODE given R$"
 130  PRINT N;
 140  RETURN 
 150  IF  NOT C THEN  GOSUB 250INITIALIZE
 160  LET J = 0
 170  LET N = 0
 180  FOR I =  LEN (R$) TO 1 STEP  - 1
 190      LET P = J
 200      FOR J = 1 TO C
 210          IF  MID$ (C$,J,1) <  >  MID$ (R$,I,1) THEN  NEXT J
 220      IF J <  = C THEN N = N + R(J) * ((J >  = P) * 2 - 1)
 230  NEXT I
 240  RETURN 
 250  READ C$
 260  LET C =  LEN (C$)
 270  DIM R(C)
 280  FOR I = 0 TO C
 290  READ R(I)
 300      NEXT I
 310  RETURN 
 320  DATA "IVXLCDM",0,1,5,10,50,100,500,1000

BASIC256

function romToDec (roman$)
    num = 0
    prenum = 0
    for i = length(roman$) to 1 step -1
        x$ = mid(roman$, i, 1)
        n = 0
        if x$ = "M" then n = 1000
        if x$ = "D" then n = 500
        if x$ = "C" then n = 100
        if x$ = "L" then n = 50
        if x$ = "X" then n = 10
        if x$ = "V" then n = 5
        if x$ = "I" then n = 1

        if n < preNum then num -= n else num += n
        preNum = n
    next i

    return num
end function

#Testing
print "MCMXCIX = "; romToDec("MCMXCIX")     #1999
print "MDCLXVI = "; romToDec("MDCLXVI")     #1666
print "XXV     = "; romToDec("XXV")         #25
print "CMLIV   = "; romToDec("CMLIV")       #954
print "MMXI    = "; romToDec("MMXI")        #2011

BBC BASIC

      PRINT "MCMXCIX", FNromandecode("MCMXCIX")
      PRINT "MMXII", FNromandecode("MMXII")
      PRINT "MDCLXVI", FNromandecode("MDCLXVI")
      PRINT "MMMDCCCLXXXVIII", FNromandecode("MMMDCCCLXXXVIII")
      END
      
      DEF FNromandecode(roman$)
      LOCAL i%, j%, p%, n%, r%()
      DIM r%(7) : r%() = 0,1,5,10,50,100,500,1000
      FOR i% = LEN(roman$) TO 1 STEP -1
        j% = INSTR("IVXLCDM", MID$(roman$,i%,1))
        IF j%=0 ERROR 100, "Invalid character"
        IF j%>=p% n% += r%(j%) ELSE n% -= r%(j%)
        p% = j%
      NEXT
      = n%
Output:
MCMXCIX         1999
MMXII           2012
MDCLXVI         1666
MMMDCCCLXXXVIII           3888

Chipmunk Basic

Through IF-THEN statements

Works with: Chipmunk Basic version 3.6.4
Works with: Applesoft BASIC
Works with: MSX_BASIC
Works with: QBasic
100 cls : rem  100 home  for Applesoft BASIC
110 roman$ = "MCMXCIX" : print roman$,"=> "; : gosub 170 : print decimal '1999
120 roman$ = "XXV" : print roman$,"=> "; : gosub 170 : print decimal     '25
130 roman$ = "CMLIV" : print roman$,"=> "; : gosub 170 : print decimal   '954
140 roman$ = "MMXI" : print roman$,"=> "; : gosub 170 : print decimal    '2011
150 end
160 rem Decode from roman
170 decimal = 0
180 predecimal = 0
190 for i = len(roman$) to 1 step -1
200  x$ = mid$(roman$,i,1)
210  if x$ = "M" then n = 1000 : goto 280
220  if x$ = "D" then n = 500 : goto 280
230  if x$ = "C" then n = 100 : goto 280
240  if x$ = "L" then n = 50 : goto 280
250  if x$ = "X" then n = 10 : goto 280
260  if x$ = "V" then n = 5 : goto 280
270  if x$ = "I" then n = 1
280  if n < predecimal then decimal = decimal-n
285  if n >= predecimal then decimal = decimal+n
290  predecimal = n
300 next i
310 return

Through SELECT CASE statement

Works with: Chipmunk Basic version 3.6.4
100 cls
110 roman$ = "MCMXCIX" : print roman$,"=> "; : gosub 170 : print decimal '1999
120 roman$ = "XXV" : print roman$,"=> "; : gosub 170 : print decimal     '25
130 roman$ = "CMLIV" : print roman$,"=> "; : gosub 170 : print decimal   '954
140 roman$ = "MMXI" : print roman$,"=> "; : gosub 170 : print decimal    '2011
150 end
160 rem Decode from roman
170 decimal = 0
180 predecimal = 0
190 for i = len(roman$) to 1 step -1
200  x$ = mid$(roman$,i,1)
210  select case x$ 
220   case "M" : n = 1000
230   case "D" : n = 500
240   case "C" : n = 100
250   case "L" : n = 50
260   case "X" : n = 10
270   case "V" : n = 5
280   case "I" : n = 1
290   case else : print "not a roman numeral" : end
300  end select
310  if n < predecimal then decimal = decimal-n else decimal = decimal+n
320  predecimal = n
330 next i
340 return

FreeBASIC

' FB 1.05.0 Win64

Function romanDecode(roman As Const String) As Integer
  If roman = "" Then Return 0  '' zero denotes invalid roman number
  Dim roman1(0 To 2) As String = {"MMM", "MM", "M"}
  Dim roman2(0 To 8) As String = {"CM", "DCCC", "DCC", "DC", "D", "CD", "CCC", "CC", "C"}
  Dim roman3(0 To 8) As String = {"XC", "LXXX", "LXX", "LX", "L", "XL", "XXX", "XX", "X"}
  Dim roman4(0 To 8) As String = {"IX", "VIII", "VII", "VI", "V", "IV", "III", "II", "I"}
  Dim As Integer i, value = 0, length = 0
  Dim r As String = UCase(roman)

  For i = 0 To 2    
    If Left(r, Len(roman1(i))) = roman1(i) Then
      value += 1000 * (3 - i)
      length = Len(roman1(i))
      r = Mid(r, length + 1)
      length = 0
      Exit For
    End If
  Next

  For i = 0 To 8 
    If Left(r, Len(roman2(i))) = roman2(i) Then
      value += 100 * (9 - i)
      length = Len(roman2(i))
      r = Mid(r, length + 1)
      length = 0
      Exit For
    End If
  Next

  For i = 0 To 8 
    If Left(r, Len(roman3(i))) = roman3(i) Then
      value += 10 * (9 - i)
      length = Len(roman3(i))
      r = Mid(r, length + 1)
      length = 0
      Exit For
    End If
  Next

  For i = 0 To 8
    If Left(r, Len(roman4(i))) = roman4(i) Then
      value += 9 - i
      length = Len(roman4(i))
      Exit For
    End If
  Next
  
  ' Can't be a valid roman number if there are any characters left 
  If Len(r) > length Then Return 0  
  Return value  
End Function

Dim a(2) As String = {"MCMXC", "MMVIII" , "MDCLXVI"}
For i As Integer = 0 To 2
  Print a(i); Tab(8); " =>"; romanDecode(a(i))
Next

Print
Print "Press any key to quit"
Sleep
Output:
MCMXC   => 1990
MMVIII  => 2008
MDCLXVI => 1666

FutureBasic

window 1

local fn RomantoDecimal( roman as CFStringRef ) as long
  long i, n, preNum = 0, num = 0
  
  for i = len(roman) - 1 to 0 step -1
    n = 0
    select ( fn StringCharacterAtIndex( roman, i ) )
      case _"M" : n = 1000
      case _"D" : n = 500
      case _"C" : n = 100
      case _"L" : n = 50
      case _"X" : n = 10
      case _"V" : n = 5
      case _"I" : n = 1
    end select
    if ( n < preNum ) then num = num - n else num = num + n
    preNum = n
  next
  
end fn = num

print @"  MCMXC = "; fn RomantoDecimal( @"MCMXC"   )
print @" MMVIII = "; fn RomantoDecimal( @"MMVIII"  )
print @"  MMXVI = "; fn RomantoDecimal( @"MMXVI"   )
print @"MDCLXVI = "; fn RomantoDecimal( @"MDCLXVI" )
print @" MCMXIV = "; fn RomantoDecimal( @"MCMXIV"  )
print @"  DXIII = "; fn RomantoDecimal( @"DXIII"   )
print @"      M = "; fn RomantoDecimal( @"M"       )
print @"  DXIII = "; fn RomantoDecimal( @"DXIII"   )
print @" XXXIII = "; fn RomantoDecimal( @"XXXIII"  )

HandleEvents
Output:
 MCMXC = 1990
 MMVIII = 2008
  MMXVI = 2016
MDCLXVI = 1666
 MCMXIV = 1914
  DXIII = 513
      M = 1000
  DXIII = 513
 XXXIII = 33

Gambas

'This code will create a GUI Form and Objects and carry out the Roman Numeral convertion as you type
'The input is case insensitive
'A basic check for invalid charaters is made

hTextBox As TextBox                                           'To allow the creation of a TextBox
hValueBox As ValueBox                                         'To allow the creation of a ValueBox

Public Sub Form_Open()                                        'Form opens..

SetUpForm                                                     'Go to the SetUpForm Routine
hTextBox.text = "MCMXC"                                       'Put a Roman numeral in the TextBox

End

Public Sub TextBoxInput_Change()                              'Each time the TextBox text changes..
Dim cRomanN As Collection = ["M": 1000, "D": 500, "C": 100, "L": 50, "X": 10, "V": 5, "I": 1]  'Collection of nemerals e.g 'M' = 1000
Dim cMinus As Collection = ["IV": -2, "IX": -2, "XL": -20, "XC": - 20, "CD": -200, "CM": -200] 'Collection of the 'one less than' numbers e.g. 'IV' = 4
Dim sClean, sTemp As String                                   'Various string variables
Dim siCount As Short                                          'Counter
Dim iTotal As Integer                                         'Stores the total of the calculation

hTextBox.Text = UCase(hTextBox.Text)                          'Make any text in the TextBox upper case

For siCount = 1 To Len(hTextBox.Text)                         'Loop through each character in the TextBox
  If InStr("MDCLXVI", Mid(hTextBox.Text, siCount, 1)) Then    'If a Roman numeral exists then..
    sClean &= Mid(hTextBox.Text, siCount, 1)                  'Put it in 'sClean' (Stops input of non Roman numerals)
  End If
Next

hTextBox.Text = sClean                                        'Put the now clean text in the TextBox

For siCount = 1 To Len(hTextBox.Text)                         'Loop through each character in the TextBox
  iTotal += cRomanN[Mid(hTextBox.Text, siCount, 1)]           'Total up all the characters, note 'IX' will = 11 not 9
Next

For Each sTemp In cMinus                                      'Loop through each item in the cMinus Collection
  If InStr(sClean, cMinus.Key) > 0 Then iTotal += Val(sTemp)  'If a 'Minus' value is in the string e.g. 'IX' which has been calculated at 11 subtract 2 = 9
Next

hValueBox.text = iTotal                                       'Display the total

End

Public Sub SetUpForm()                                        'Create the Objects for the Form
Dim hLabel1, hLabel2 As Label                                 'For 2 Labels

Me.height = 150                                               'Form Height
Me.Width = 300                                                'Form Width
Me.Padding = 20                                               'Form padding (border)
Me.Text = "Roman Numeral converter"                           'Text in Form header
Me.Arrangement = Arrange.Vertical                             'Form arrangement

hLabel1 = New Label(Me)                                       'Create a Label
hLabel1.Height = 21                                           'Label Height
hLabel1.expand = True                                         'Expand the Label
hLabel1.Text = "Enter a Roman numeral"                        'Put text in the Label

hTextBox = New TextBox(Me) As "TextBoxInput"                  'Set up a TextBox with an Event Label
hTextBox.Height = 21                                          'TextBox height
hTextBox.expand = True                                        'Expand the TextBox

hLabel2 = New Label(Me)                                       'Create a Label
hLabel2.Height = 21                                           'Label Height
hLabel2.expand = True                                         'Expand the Label
hLabel2.Text = "The decimal equivelent is: -"                 'Put text in the Label

hValueBox = New ValueBox(Me)                                  'Create a ValueBox
hValueBox.Height = 21                                         'ValuBox Height
hValueBox.expand = True                                       'Expand the ValueBox
hValueBox.ReadOnly = True                                     'Set ValueBox to Read Only

End

Click here for image of running code

GW-BASIC

The Chipmunk Basic through IF-THEN statements solution works without any changes.

Liberty BASIC

As Fortran & PureBasic.

  print "MCMXCIX = "; romanDec( "MCMXCIX") '1999
  print "MDCLXVI = "; romanDec( "MDCLXVI") '1666
  print "XXV     = "; romanDec( "XXV")     '25
  print "CMLIV   = "; romanDec( "CMLIV")   '954
  print "MMXI    = "; romanDec( "MMXI")    '2011

  end

function romanDec( roman$)
  arabic  =0
  lastval =0

  for i = len( roman$) to 1 step -1
    select case upper$( mid$( roman$, i, 1))
      case "M"
        n = 1000
      case "D"
        n = 500
      case "C"
        n = 100
      case "L"
        n = 50
      case "X"
        n = 10
      case "V"
        n = 5
      case "I"
        n = 1
      case else
        n = 0
    end select

    if n <lastval then
      arabic =arabic -n
    else
      arabic =arabic +n
    end if

    lastval =n
  next

  romanDec =arabic
end function
Output:
MCMXCIX = 1999
MDCLXVI = 1666
XXV     = 25
CMLIV   = 954
MMXI    = 2011

MSX Basic

The Chipmunk Basic through IF-THEN statements solution works without any changes.

PureBasic

Procedure romanDec(roman.s)
  Protected i, n, lastval, arabic
    
  For i = Len(roman) To 1 Step -1
    Select UCase(Mid(roman, i, 1))
      Case "M"
        n = 1000
      Case "D"
        n = 500
      Case "C"
        n = 100
      Case "L"
        n = 50
      Case "X"
        n = 10
      Case "V"
        n = 5
      Case "I"
        n = 1
      Default
        n = 0
    EndSelect
    If (n < lastval)
      arabic - n
    Else
      arabic + n
    EndIf
    lastval = n
  Next 
  
  ProcedureReturn arabic
EndProcedure

If OpenConsole()
  PrintN(Str(romanDec("MCMXCIX"))) ;1999
  PrintN(Str(romanDec("MDCLXVI"))) ;1666
  PrintN(Str(romanDec("XXV")))     ;25
  PrintN(Str(romanDec("CMLIV")))   ;954
  PrintN(Str(romanDec("MMXI")))    ;2011
  
  Print(#CRLF$ + #CRLF$ + "Press ENTER to exit"): Input()
  CloseConsole()
EndIf
Output:
1999
1666
25
954
2011

QBasic

FUNCTION romToDec (roman$)
    num = 0
    prenum = 0
    FOR i = LEN(roman$) TO 1 STEP -1
        x$ = MID$(roman$, i, 1)
        n = 0
        IF x$ = "M" THEN n = 1000
        IF x$ = "D" THEN n = 500
        IF x$ = "C" THEN n = 100
        IF x$ = "L" THEN n = 50
        IF x$ = "X" THEN n = 10
        IF x$ = "V" THEN n = 5
        IF x$ = "I" THEN n = 1

        IF n < preNum THEN num = num - n ELSE num = num + n
        preNum = n
    NEXT i

    romToDec = num
END FUNCTION

!Testing
PRINT "MCMXCIX = "; romToDec("MCMXCIX")     '1999
PRINT "MDCLXVI = "; romToDec("MDCLXVI")     '1666
PRINT "XXV     = "; romToDec("XXV")         '25
PRINT "CMLIV   = "; romToDec("CMLIV")       '954
PRINT "MMXI    = "; romToDec("MMXI")        '2011

QB64

SCREEN _NEWIMAGE(400, 600, 32)


CLS


Main:
'------------------------------------------------
' CALLS THE romToDec FUNCTION WITH THE ROMAN
' NUMERALS AND RETURNS ITS DECIMAL EQUIVELENT.
'
   PRINT "ROMAN NUMERAL TO DECIMAL CONVERSION"
   PRINT: PRINT

   PRINT "MDCCIV  = "; romToDec("MDCCIV") '1704
   PRINT "MCMXC   = "; romToDec("MCMXC") '1990
   PRINT "MMVIII  = "; romToDec("MMVIII") '2008
   PRINT "MDCLXVI = "; romToDec("MDCLXVI") '1666
   PRINT: PRINT
   PRINT "Here are other solutions not from the TASK:"
   PRINT "MCMXCIX = "; romToDec("MCMXCIX") '1999
   PRINT "XXV     = "; romToDec("XXV") '25
   PRINT "CMLIV   = "; romToDec("CMLIV") '954
   PRINT "MMXI    = "; romToDec("MMXI") '2011
   PRINT "MMIIIX  = "; romToDec("MMIIIX") '2011
   PRINT: PRINT
   PRINT "2011 can be written either as MMXI or MMIIIX"
   PRINT "With the IX = 9, MMIIIX is also 2011."
   PRINT "2011 IS CORRECT (MM=2000 + II = 2 + IX = 9)"

   END



FUNCTION romToDec (roman AS STRING)
'------------------------------------------------------
' FUNCTION THAT CONVERTS ANY ROMAN NUMERAL TO A DECIMAL
'
    prenum = 0: num = 0
    LN = LEN(roman)
    FOR i = LN TO 1 STEP -1
        x$ = MID$(roman, i, 1)
        n = 1000
        SELECT CASE x$
               CASE "M": n = n / 1
               CASE "D": n = n / 2
               CASE "C": n = n / 10
               CASE "L": n = n / 20
               CASE "X": n = n / 100
               CASE "V": n = n / 200
               CASE "I": n = n / n
               CASE ELSE: n = 0
        END SELECT
        IF n < prenum THEN num = num - n ELSE num = num + n
        prenum = n
   NEXT i

   romToDec = num

END FUNCTION

Run BASIC

print "MCMXCIX = "; romToDec( "MCMXCIX") '1999
print "MDCLXVI = "; romToDec( "MDCLXVI") '1666
print "XXV     = "; romToDec( "XXV")     '25
print "CMLIV   = "; romToDec( "CMLIV")   '954
print "MMXI    = "; romToDec( "MMXI")    '2011

function romToDec(roman$)
  for i  = len(roman$) to 1 step -1
   x$    = mid$(roman$, i, 1)
   n     = 0
   if x$ = "M" then n = 1000
   if x$ = "D" then n = 500
   if x$ = "C" then n = 100
   if x$ = "L" then n = 50
   if x$ = "X" then n = 10
   if x$ = "V" then n = 5
   if x$ = "I" then n = 1
 
  if n < preNum then num = num - n else num = num + n
  preNum = n
  next
 
  romToDec =num
end function

TechBASIC

Main:
!------------------------------------------------
! CALLS THE romToDec FUNCTION WITH THE ROMAN
! NUMERALS AND RETURNS ITS DECIMAL EQUIVELENT.
!
   
   PRINT "MCMXC   = "; romToDec("MCMXC")   !1990
   PRINT "MMVIII  = "; romToDec("MMVIII")  !2008
   PRINT "MDCLXVI = "; romToDec("MDCLXVI") !1666
   PRINT:PRINT
   PRINT "Here are other solutions not from the TASK:"
   PRINT "MCMXCIX = "; romToDec("MCMXCIX") !1999
   PRINT "XXV     = "; romToDec("XXV")     !25
   PRINT "CMLIV   = "; romToDec("CMLIV")   !954
   PRINT "MMXI    = "; romToDec("MMXI")    !2011
   PRINT:PRINT
   PRINT "Without error checking, this also is 2011, but is wrong"
   PRINT "MMIIIX  = "; romToDec("MMIIIX")  !INVAID, 2011
 
STOP


FUNCTION romToDec(roman AS STRING) AS INTEGER
!------------------------------------------------------
! FUNCTION THAT CONVERTS ANY ROMAN NUMERAL TO A DECIMAL
!
    prenum=0!num=0
    ln=LEN(roman)
    FOR i=ln TO 1 STEP -1
        x$=MID(roman,i,1)
        n=1000
        SELECT CASE x$
               CASE "M":n=n/1
               CASE "D":n=n/2
               CASE "C":n=n/10
               CASE "L":n=n/20
               CASE "X":n=n/100
               CASE "V":n=n/200
               CASE "I":n=n/n
               CASE ELSE:n=0
        END SELECT
        IF n < preNum THEN num=num-n ELSE num=num+n
        preNum=n
   next i
 
   romToDec=num

END FUNCTION
Output:
MCMXC   = 1990
MMVIII  = 2008
MDCLXVI = 1666


Here are other solutions not from the TASK:
MCMXCIX = 1999
XXV     = 25
CMLIV   = 954
MMXI    = 2011


Without error checking, this also is 2011, but is wrong
MMIIIX  = 2011

TI-83 BASIC

Using the Rom‣Dec function "real(21," from Omnicalc.

PROGRAM:ROM2DEC
:Input Str1
:Disp real(21,Str1)

Using TI-83 BASIC

PROGRAM:ROM2DEC
:Input "ROMAN:",Str1
:{1000,500,100,50,10,5,1}➞L1
:0➞P
:0➞Y
:For(I,length(Str1),1,-1)
  :inString("MDCLXVI",sub(Str1,I,1))➞X
  :If X≤0:Then
    :Disp "BAD NUMBER"
    :Stop
  :End
  :L1(x)➞N
  :If N<P:Then
    :Y–N➞Y
  :Else
    :Y+N➞Y
  :End
  :N➞P
:End
:Disp Y

True BASIC

FUNCTION romtodec(roman$)
    LET num = 0
    LET prenum = 0
    FOR i = len(roman$) to 1 step -1
        LET x$ = (roman$)[i:i+1-1]
        LET n = 0
        IF x$ = "M" then LET n = 1000
        IF x$ = "D" then LET n = 500
        IF x$ = "C" then LET n = 100
        IF x$ = "L" then LET n = 50
        IF x$ = "X" then LET n = 10
        IF x$ = "V" then LET n = 5
        IF x$ = "I" then LET n = 1
        IF n < prenum then LET num = num-n else LET num = num+n
        LET prenum = n
    NEXT i

    LET romtodec = num
END FUNCTION

!Testing
PRINT "MCMXCIX = "; romToDec("MCMXCIX")     !1999
PRINT "MDCLXVI = "; romToDec("MDCLXVI")     !1666
PRINT "XXV     = "; romToDec("XXV")         !25
PRINT "CMLIV   = "; romToDec("CMLIV")       !954
PRINT "MMXI    = "; romToDec("MMXI")        !2011
END

XBasic

Works with: Windows XBasic
PROGRAM  "romandec"
VERSION  "0.0000"

DECLARE FUNCTION Entry ()
DECLARE FUNCTION romToDec (roman$)

FUNCTION  Entry ()
  PRINT "MCMXCIX = "; romToDec("MCMXCIX")
  PRINT "MDCLXVI = "; romToDec("MDCLXVI")
  PRINT "XXV     = "; romToDec("XXV")
  PRINT "CMLIV   = "; romToDec("CMLIV")
  PRINT "MMXI    = "; romToDec("MMXI")
END FUNCTION

FUNCTION romToDec (roman$)
  num = 0
  prenum = 0
  FOR i = LEN(roman$) TO 1 STEP -1
    x$ = MID$(roman$, i, 1)
	SELECT CASE x$ 
	  CASE "M" : n = 1000
	  CASE "D" : n = 500
	  CASE "C" : n = 100
	  CASE "L" : n = 50
	  CASE "X" : n = 10
	  CASE "V" : n = 5
	  CASE "I" : n = 1
	END SELECT
    IF n < prenum THEN num = num-n ELSE num = num+n
    prenum = n
  NEXT i

  RETURN num
END FUNCTION
END PROGRAM

Yabasic

romans$ = "MDCLXVI"
decmls$ = "1000,500,100,50,10,5,1"

sub romanDec(s$)
    local i, n, prev, res, decmls$(1)
	
    n = token(decmls$, decmls$(), ",")
	
    for i = len(s$) to 1 step -1
        n = val(decmls$(instr(romans$, mid$(s$, i, 1))))
        if n < prev n = 0 - n
        res = res + n
        prev = n
    next i
    return res
end sub
 
? romanDec("MCMXCIX")               // 1999
? romanDec("MDCLXVI")               // 1666
? romanDec("XXV")                   // 25
? romanDec("XIX")                   // 19
? romanDec("XI")                    // 11
? romanDec("CMLIV")                 // 954
? romanDec("MMXI")                  // 2011
? romanDec("CD")                    // 400
? romanDec("MCMXC")                 // 1990
? romanDec("MMVIII")                // 2008
? romanDec("MMIX")                  // 2009
? romanDec("MDCLXVI")               // 1666
? romanDec("MMMDCCCLXXXVIII")       // 3888

Arturo

syms: #[ M: 1000, D: 500, C: 100, L: 50, X: 10, V: 5, I: 1 ]

fromRoman: function [roman][
	ret: 0
	loop 0..(size roman)-2 'ch [
		fst: roman\[ch]
		snd: roman\[ch+1]
		valueA: syms\[fst]
		valueB: syms\[snd]

		if? valueA < valueB -> ret: ret - valueA
	 	else				-> ret: ret + valueA
	]
	return ret + syms\[last roman]
]

loop ["MCMXC" "MMVIII" "MDCLXVI"] 'r -> print [r "->" fromRoman r]
Output:
MCMXC -> 1990 
MMVIII -> 2008 
MDCLXVI -> 1666

AutoHotkey

Works with: AutoHotkey_L
Roman_Decode(str){
	res := 0
	Loop Parse, str
	{
		n := {M: 1000, D:500, C:100, L:50, X:10, V:5, I:1}[A_LoopField]
		If ( n > OldN ) && OldN
			res -= 2*OldN
		res += n, oldN := n
	}
	return res
}

test = MCMXC|MMVIII|MDCLXVI
Loop Parse, test, |
   res .= A_LoopField "`t= " Roman_Decode(A_LoopField) "`r`n"
clipboard := res
Output:
MCMXC	= 1990
MMVIII	= 2008
MDCLXVI	= 1666

AWK

# syntax: GAWK -f ROMAN_NUMERALS_DECODE.AWK
BEGIN {
    leng = split("MCMXC MMVIII MDCLXVI",arr," ")
    for (i=1; i<=leng; i++) {
      n = arr[i]
      printf("%s = %s\n",n,roman2arabic(n))
    }
    exit(0)
}
function roman2arabic(r,  a,i,p,q,u,ua,una,unr) {
    r = toupper(r)
    unr = "MDCLXVI" # each Roman numeral in descending order
    una = "1000 500 100 50 10 5 1" # and its Arabic equivalent
    split(una,ua," ")
    i = split(r,u,"")
    a = ua[index(unr,u[i])]
    while (--i) {
      p = index(unr,u[i])
      q = index(unr,u[i+1])
      a += ua[p] * ((p>q) ? -1 : 1)
    }
    return( (a>0) ? a : "" )
}
Output:
MCMXC = 1990
MMVIII = 2008
MDCLXVI = 1666

Batch File

Translation of: Fortran
@echo off
setlocal enabledelayedexpansion

::Testing...
call :toArabic MCMXC
echo MCMXC = !arabic!
call :toArabic MMVIII
echo MMVIII = !arabic!
call :toArabic MDCLXVI
echo MDCLXVI = !arabic!
call :toArabic CDXLIV
echo CDXLIV = !arabic!
call :toArabic XCIX
echo XCIX = !arabic!
pause>nul
exit/b 0

::The "function"...
:toArabic
set roman=%1
set arabic=
set lastval=
	%== Alternative for counting the string length ==%
set leng=-1
for /l %%. in (0,1,1000) do set/a leng+=1&if "!roman:~%%.,1!"=="" goto break
:break
set /a last=!leng!-1
for /l %%i in (!last!,-1,0) do (
	set n=0
	if /i "!roman:~%%i,1!"=="M" set n=1000
	if /i "!roman:~%%i,1!"=="D" set n=500
	if /i "!roman:~%%i,1!"=="C" set n=100
	if /i "!roman:~%%i,1!"=="L" set n=50
	if /i "!roman:~%%i,1!"=="X" set n=10
	if /i "!roman:~%%i,1!"=="V" set n=5
	if /i "!roman:~%%i,1!"=="I" set n=1
	
	if !n! lss !lastval! (
		set /a arabic-=n
	) else (
		set /a arabic+=n
	)
	set lastval=!n!
)
goto :EOF
Output:
MCMXC = 1990
MMVIII = 2008
MDCLXVI = 1666
CDXLIV = 444
XCIX = 99

BCPL

get "libhdr"

let roman(s) = valof
$(  let digit(ch) = valof
    $(  let ds = table 'm','d','c','l','x','v','i'
        let vs = table 1000,500,100,50,10,5,1
        for i=0 to 6
            if ds!i=(ch|32) then resultis vs!i
        resultis 0
    $)
    let acc = 0
    for i=1 to s%0
    $(  let d = digit(s%i)
        if d=0 then resultis 0
        test i<s%0 & d<digit(s%(i+1))
            do acc := acc-d
            or acc := acc+d
    $)
    resultis acc
$)

let show(s) be writef("%S: %N*N", s, roman(s))
    
let start() be
$(  show("MCMXC")
    show("MDCLXVI")
    show("MMVII")
    show("MMXXI")
$)
Output:
MCMXC: 1990
MDCLXVI: 1666
MMVII: 2007
MMXXI: 2021

BQN

ToArabicA  {
  c  "IVXLCDM"         # Characters
  v   (104) × 15  # Their values
  A  +´(⊢ׯ1<«) v ˜ c  
}
Example use:
   ToArabic¨ "MCMXC""MDCLXVI""MMVII""MMXXI"
 1990 1666 2007 2021 

Bracmat

Translation of: Icon and Unicon
  ( unroman
  =   nbr,lastVal,val
    .     0:?nbr:?lastVal
        & @( low$!arg
           :   ?
               %@?L
               ( ?
               &     (m.1000)
                     (d.500)
                     (c.100)
                     (l.50)
                     (x.10)
                     (v.5)
                     (i.1)
                 : ? (!L.?val) ?
               &     (!val:~>!lastVal|!val+-2*!lastVal)
                   + !nbr
                 : ?nbr
               & !val:?lastVal
               & ~
               )
           )
      | !nbr
  )
&     (M.1000)
      (MCXI.1111)
      (CMXI.911)
      (MCM.1900)
      (MCMXC.1990)
      (MMVIII.2008)
      (MMIX.2009)
      (MCDXLIV.1444)
      (MDCLXVI.1666)
      (MMXII.2012)
  : ?years
& (test=.out$(!arg unroman$!arg))
& (   !years
    : ? (?L.?D) (?&test$!L&~)
  | done
  );
Output:
M 1000
MCXI 1111
CMXI 911
MCM 1900
MCMXC 1990
MMVIII 2008
MMIX 2009
MCDXLIV 1444
MDCLXVI 1666
MMXII 2012

C

Note: the code deliberately did not distinguish between "I", "J" or "U", "V", doing what Romans did for fun.

#include <stdio.h>

int digits[26] = { 0, 0, 100, 500, 0, 0, 0, 0, 1, 1, 0, 50, 1000, 0, 0, 0, 0, 0, 0, 0, 5, 5, 0, 10, 0, 0 };

/* assuming ASCII, do upper case and get index in alphabet. could also be
        inline int VALUE(char x) { return digits [ (~0x20 & x) - 'A' ]; }
   if you think macros are evil */
#define VALUE(x) digits[(~0x20 & (x)) - 'A']

int decode(const char * roman)
{
        const char *bigger;
        int current;
        int arabic = 0;
        while (*roman != '\0') {
                current = VALUE(*roman);
                /*      if (!current) return -1;
                        note: -1 can be used as error code; Romans didn't even have zero
                */
                bigger = roman;

                /* look for a larger digit, like IV or XM */
                while (VALUE(*bigger) <= current && *++bigger != '\0');

                if (*bigger == '\0')
                        arabic += current;
                else {
                        arabic += VALUE(*bigger);
                        while (roman < bigger)
                                arabic -= VALUE(* (roman++) );
                }

                roman ++;
        }
        return arabic;
}

int main()
{
        const char * romans[] = { "MCmxC", "MMVIII", "MDClXVI", "MCXLUJ" };
        int i;

        for (i = 0; i < 4; i++)
                printf("%s\t%d\n", romans[i], decode(romans[i]));

        return 0;
}

C#

using System;
using System.Collections.Generic;

namespace Roman
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            // Decode and print the numerals.
            Console.WriteLine("{0}: {1}", "MCMXC", Decode("MCMXC"));
            Console.WriteLine("{0}: {1}", "MMVIII", Decode("MMVIII"));
            Console.WriteLine("{0}: {1}", "MDCLXVI", Decode("MDCLXVI"));
        }

        // Dictionary to hold our numerals and their values.
        private static readonly Dictionary<char, int> RomanDictionary = new Dictionary<char, int>
                                                                            {
                                                                                {'I', 1},
                                                                                {'V', 5},
                                                                                {'X', 10},
                                                                                {'L', 50},
                                                                                {'C', 100},
                                                                                {'D', 500},
                                                                                {'M', 1000}
                                                                            };

        private static int Decode(string roman)
        {
            /* Make the input string upper-case,
             * because the dictionary doesn't support lower-case characters. */
            roman = roman.ToUpper();

            /* total = the current total value that will be returned.
             * minus = value to subtract from next numeral. */
            int total = 0, minus = 0;

            for (int i = 0; i < roman.Length; i++) // Iterate through characters.
            {
                // Get the value for the current numeral. Takes subtraction into account.
                int thisNumeral = RomanDictionary[roman[i]] - minus;

                /* Checks if this is the last character in the string, or if the current numeral
                 * is greater than or equal to the next numeral. If so, we will reset our minus
                 * variable and add the current numeral to the total value. Otherwise, we will
                 * subtract the current numeral from the next numeral, and continue. */
                if (i >= roman.Length - 1 ||
                    thisNumeral + minus >= RomanDictionary[roman[i + 1]])
                {
                    total += thisNumeral;
                    minus = 0;
                }
                else
                {
                    minus = thisNumeral;
                }
            }

            return total; // Return the total.
        }
    }
}
Output:
MCMXC: 1990
MMVIII: 2008
MDCLXVI: 1666

C++

#include <exception>
#include <string>
#include <iostream>
using namespace std;

namespace Roman
{
	int ToInt(char c)
	{
		switch (c)
		{
			case 'I':  return 1;
			case 'V':  return 5;
			case 'X':  return 10;
			case 'L':  return 50;
			case 'C':  return 100;
			case 'D':  return 500;
			case 'M':  return 1000;
		}
		throw exception("Invalid character");
	}

	int ToInt(const string& s)
	{
		int retval = 0, pvs = 0;
		for (auto pc = s.rbegin(); pc != s.rend(); ++pc)
		{
			const int inc = ToInt(*pc);
			retval += inc < pvs ? -inc : inc;
			pvs = inc;
		}
		return retval;
	}
}

int main(int argc, char* argv[])
{
	try
	{
		cout << "MCMXC = " << Roman::ToInt("MCMXC") << "\n";
		cout << "MMVIII = " << Roman::ToInt("MMVIII") << "\n";
		cout << "MDCLXVI = " << Roman::ToInt("MDCLXVI") << "\n";
	}
	catch (exception& e)
	{
		cerr << e.what();
		return -1;
	}
	return 0;
}
Output:
MCMXC = 1990
MMVIII = 2008
MDCLXVI = 1666

Ceylon

shared void run() {
	
	value numerals = map {
		'I' -> 1,
		'V' -> 5,
		'X' -> 10,
		'L' -> 50,
		'C' -> 100,
		'D' -> 500,
		'M' -> 1000
	};
	
	function toHindu(String roman) {
		variable value total = 0;
		for(i->c in roman.indexed) {
			assert(exists currentValue = numerals[c]); 
			/* Look at the next letter to see if we're looking
			 at a IV or CM or whatever. If so subtract the
			 current number from the total. */
			if(exists next = roman[i + 1],             
				exists nextValue = numerals[next],     
				currentValue < nextValue) {            
				total -= currentValue;                 
			} else {
				total += currentValue;
			}
		}
		return total;
	}
	
	assert(toHindu("I") == 1);
	assert(toHindu("II") == 2);
	assert(toHindu("IV") == 4);
	assert(toHindu("MDCLXVI") == 1666);
	assert(toHindu("MCMXC") == 1990);
	assert(toHindu("MMVIII") == 2008);
}

Clojure

;; Incorporated some improvements from the alternative implementation below
(defn ro2ar [r]
  (->> (reverse (.toUpperCase r))
       (map {\M 1000 \D 500 \C 100 \L 50 \X 10 \V 5 \I 1})
       (partition-by identity)
       (map (partial apply +))
       (reduce #(if (< %1 %2) (+ %1 %2) (- %1 %2)))))

;; alternative
(def numerals { \I 1, \V 5, \X 10, \L 50, \C 100, \D 500, \M 1000})
(defn from-roman [s] 
  (->> s .toUpperCase 
    (map numerals) 
    (reduce (fn [[sum lastv] curr] [(+ sum curr (if (< lastv curr) (* -2 lastv) 0)) curr]) [0,0]) 
    first))
Output:
(map ro2ar ["MDCLXVI" "MMMCMXCIX" "XLVIII" "MMVIII"])
(1666 3999 48 2008)

CLU

roman = cluster is decode
    rep = null
    
    digit_value = proc (c: char) returns (int) signals (invalid)
        if c < 'a' then c := char$i2c(char$c2i(c) + 32) end 
        if     c = 'm' then return(1000)
        elseif c = 'd' then return(500)
        elseif c = 'c' then return(100)
        elseif c = 'l' then return(50)
        elseif c = 'x' then return(10)
        elseif c = 'v' then return(5)
        elseif c = 'i' then return(1)
        else signal invalid
        end
    end digit_value
    
    decode = proc (s: string) returns (int) signals (invalid)
        acc: int := 0
        for i: int in int$from_to(1, string$size(s)) do
            d: int := digit_value(s[i])
            if i < string$size(s) cand d < digit_value(s[i+1]) then
                acc := acc - d
            else
                acc := acc + d
            end
        end resignal invalid
        return(acc)
    end decode
end roman

start_up = proc ()
    po: stream := stream$primary_output()
    tests: array[string] := array[string]$
        ["MCMXC", "mdclxvi", "MmViI", "mmXXi", "INVALID"]
    
    for test: string in array[string]$elements(tests) do
        stream$puts(po, test || ": ")
        stream$putl(po, int$unparse(roman$decode(test))) except when invalid:
            stream$putl(po, "not a valid Roman numeral!")
        end
    end
end start_up
Output:
MCMXC: 1990
mdclxvi: 1666
MmViI: 2007
mmXXi: 2021
INVALID: not a valid Roman numeral!

COBOL

      IDENTIFICATION DIVISION.
      PROGRAM-ID.  UNROMAN.
      DATA DIVISION.
      WORKING-STORAGE SECTION.
      01  filler.
        03  i              pic 9(02) comp.
        03  j              pic 9(02) comp.
        03  k              pic 9(02) comp.
        03  l              pic 9(02) comp.
      01  inp-roman.
        03  inp-rom-ch     pic x(01) occurs 20 times.
      01  inp-roman-digits.
        03  inp-rom-digit  pic 9(01) occurs 20 times.
      01  ws-search-idx        pic 9(02) comp.
      01  ws-tbl-table-def.
        03  filler pic x(05) value '1000M'.
        03  filler pic x(05) value '0500D'.
        03  filler pic x(05) value '0100C'.
        03  filler pic x(05) value '0050L'.
        03  filler pic x(05) value '0010X'.
        03  filler pic x(05) value '0005V'.
        03  filler pic x(05) value '0001I'.
      01  filler redefines ws-tbl-table-def.
        03  ws-tbl-roman      occurs 07 times indexed by rx.
          05  ws-tbl-rom-val  pic 9(04).
          05  ws-tbl-rom-ch   pic x(01).
      01  ws-number           pic s9(05) value 0.
      01  ws-number-pic       pic zzzz9-.

      PROCEDURE DIVISION.
          accept inp-roman 
          perform
          until inp-roman = ' '
            move zeroes to inp-roman-digits
            perform
            varying i from 1 by +1 until inp-rom-ch (i) = ' '
              set rx to 1
              search ws-tbl-roman
                at end
                  move 0 to inp-rom-digit (i)
                when ws-tbl-rom-ch (rx) = inp-rom-ch (i)
                  set inp-rom-digit (i) to rx
              end-search
            end-perform
            compute l = i - 1
            move 0 to ws-number
            perform
            varying i from 1 by +1
            until i > l or inp-rom-digit (i) = 0
              compute j = inp-rom-digit (i)
              compute k = inp-rom-digit (i + 1)
              if ws-tbl-rom-val (k)
              >  ws-tbl-rom-val (j)
                compute ws-number
                =      ws-number
                -      ws-tbl-rom-val (j)
              else
                compute ws-number
                =      ws-number
                +      ws-tbl-rom-val (j)
              end-if
            end-perform
            move ws-number to ws-number-pic
            display '----------'
            display 'roman=' inp-roman 
            display 'arabic=' ws-number-pic
            if i < l or ws-number = 0                             
              display 'invalid/incomplete roman numeral at pos 'i 
                      ' found ' inp-rom-ch (i)                    
            end-if                                 
            accept inp-roman
          end-perform
          stop run
          .
      END PROGRAM UNROMAN.
Output:

input was supplied via STDIN

----------
roman=MCMLXXXVIII
        
arabic= 1988 
----------
roman=MIX
                
arabic= 1009 
----------
roman=MDCCCLXXXVII
       
arabic= 1887 
----------
roman=IX
                 
arabic=    9 
----------
roman=MMMDCCCLXXXVIII
    
arabic= 3888 
----------
roman=K
                  
arabic=    0 
invalid/incomplete roman numeral at pos 01 found K
----------
roman=MIXT
               
arabic= 1009 
invalid/incomplete roman numeral at pos 04 found T
----------
roman=MCMB
               
arabic= 1900 
invalid/incomplete roman numeral at pos 04 found B

CoffeeScript

roman_to_demical = (s) ->         
  # s is well-formed Roman Numeral >= I
  numbers =
    M: 1000
    D: 500
    C: 100
    L: 50
    X: 10
    V: 5
    I: 1

  result = 0
  for c in s
    num = numbers[c]
    result += num
    if old_num < num
      # If old_num exists and is less than num, then
      # we need to subtract it twice, once because we
      # have already added it on the last pass, and twice
      # to conform to the Roman convention that XC = 90,
      # not 110.
      result -= 2 * old_num
    old_num = num
  result
  
tests =
  IV: 4
  XLII: 42
  MCMXC: 1990
  MMVIII: 2008
  MDCLXVI: 1666

for roman, expected of tests
  dec = roman_to_demical(roman)
  console.log "error" if dec != expected
  console.log "#{roman} = #{dec}"

Common Lisp

(defun mapcn (chars nums string)
  (loop as char across string as i = (position char chars) collect (and i (nth i nums))))

(defun parse-roman (R)
  (loop with nums = (mapcn "IVXLCDM" '(1 5 10 50 100 500 1000) R)
        as (A B) on nums if A sum (if (and B (< A B)) (- A) A)))

Description:

Mapcn is a function to map characters to numbers.  It uses the mapping between its first two arguments, chars and nums,
to map its 3rd argument, string, to a list of numbers.  If a character of string is missing from chars, its number will be
nil.

Parse-roman uses mapcn to map R to a list of numbers, then iterates that list with A and B, adding A to the total whenever
it's not less than B, and subtracting it when it is.  If A is nil, it's skipped.  Such as when the character is not Roman.
If B is nil, A is added and not subtracted.  Such as at the end of the list, or when a non-Roman character, such as a space,
is embedded in the Roman.

Test code:

(dolist (r '("MCMXC" "MDCLXVI" "MMVIII"))
  (format t "~a:~10t~d~%" r (parse-roman r)))
Output:
MCMXC:    1990
MDCLXVI:  1666
MMVIII:   2008

Cowgol

include "cowgol.coh";
include "argv.coh";

# Decode the Roman numeral in the given string.
# Returns 0 if the string does not contain a valid Roman numeral.
sub romanToDecimal(str: [uint8]): (rslt: uint16) is
    # Look up a Roman digit
    sub digit(char: uint8): (val: uint16) is
        # Definition of Roman numerals
        record RomanDigit is
            char: uint8;
            value: uint16;
        end record;
    
        var digits: RomanDigit[] := {
            {'I',1}, {'V',5}, {'X',10}, {'L',50},
            {'C',100}, {'D',500}, {'M',1000}
        };
        
        char := char & ~32; # make uppercase
        
        # Look up given digit
        var i: @indexof digits := 0;
        while i < @sizeof digits loop
            val := digits[i].value;
            if digits[i].char == char then
                return;
            end if;
            i := i + 1;
        end loop;
        val := 0;
    end sub;
            
    rslt := 0;
    while [str] != 0 loop
        var cur := digit([str]); # get value of current digit
        if cur == 0 then rslt := 0; return; end if; # stop when invalid
        str := @next str;
        if digit([str]) > cur then
            # a digit followed by a larger digit should be subtracted from
            # the total
            rslt := rslt - cur;
        else
            rslt := rslt + cur;
        end if;
    end loop;
end sub;

# Read a Roman numeral from the command line and print its output
ArgvInit();
var argmt := ArgvNext();
if argmt == (0 as [uint8]) then
    # No argument
    print("No argument\n");
    ExitWithError();
end if;

print_i16(romanToDecimal(argmt));
print_nl();
Output:
$ ./romandec.386 MCMXC
1990
$ ./romandec.386 MDCLXVI
1666
$ ./romandec.386 MMVII
2007

D

import std.regex, std.algorithm;

int toArabic(in string s) /*pure nothrow*/ {
    static immutable weights = [1000, 900, 500, 400, 100,
                                90, 50, 40, 10, 9, 5, 4, 1];
    static immutable symbols = ["M","CM","D","CD","C","XC",
                                "L","XL","X","IX","V","IV","I"];

    int arabic;
    foreach (m; s.matchAll("CM|CD|XC|XL|IX|IV|[MDCLXVI]".regex))
        arabic += weights[symbols.countUntil(m.hit)];
    return arabic;
}

void main() {
    assert("MCMXC".toArabic == 1990);
    assert("MMVIII".toArabic == 2008);
    assert("MDCLXVI".toArabic == 1666);
}

Alternative more functional version:

import std.regex, std.algorithm;

immutable uint[string] w2s;

pure nothrow static this() {
    w2s = ["IX":  9, "C":  100, "D":  500, "CM": 900, "I":   1,
           "XC": 90, "M": 1000, "L":   50, "CD": 400, "XL": 40,
           "V":   5, "X":   10, "IV":   4];
}

uint toArabic(in string s) /*pure nothrow*/ @safe /*@nogc*/ {
    return s
           .matchAll("CM|CD|XC|XL|IX|IV|[MDCLXVI]".regex)
           .map!(m => w2s[m.hit])
           .sum;
}

void main() {
    assert("MCMXC".toArabic == 1990);
    assert("MMVIII".toArabic == 2008);
    assert("MDCLXVI".toArabic == 1666);
}

Delphi/Pascal

program RomanNumeralsDecode;

{$APPTYPE CONSOLE}

function RomanToInteger(const aRoman: string): Integer;
  function DecodeRomanDigit(aChar: Char): Integer;
  begin
    case aChar of
      'M', 'm': Result := 1000;
      'D', 'd': Result := 500;
      'C', 'c': Result := 100;
      'L', 'l': Result := 50;
      'X', 'x': Result := 10;
      'V', 'v': Result := 5;
      'I', 'i': Result := 1
    else
      Result := 0;
    end;
  end;

var
  i: Integer;
  lCurrVal: Integer;
  lLastVal: Integer;
begin
  Result := 0;

  lLastVal := 0;
  for i := Length(aRoman) downto 1 do
  begin
    lCurrVal := DecodeRomanDigit(aRoman[i]);
    if lCurrVal < lLastVal then
      Result := Result - lCurrVal
    else
      Result := Result + lCurrVal;
    lLastVal := lCurrVal;
  end;
end;

begin
  Writeln(RomanToInteger('MCMXC'));    // 1990
  Writeln(RomanToInteger('MMVIII'));   // 2008
  Writeln(RomanToInteger('MDCLXVI'));  // 1666
end.

EasyLang

func rom2dec rom$ .
   symbols$[] = [ "M" "D" "C" "L" "X" "V" "I" ]
   values[] = [ 1000 500 100 50 10 5 1 ]
   val = 0
   for dig$ in strchars rom$
      for i = 1 to len symbols$[]
         if symbols$[i] = dig$
            v = values[i]
         .
      .
      val += v
      if oldv < v
         val -= 2 * oldv
      .
      oldv = v
   .
   return val
.
print rom2dec "MCMXC"
print rom2dec "MMVIII"
print rom2dec "MDCLXVI"

ECL

The best declarative approach:

MapChar(STRING1 c) := CASE(c,'M'=>1000,'D'=>500,'C'=>100,'L'=>50,'X'=>10,'V'=>5,'I'=>1,0);

RomanDecode(STRING s) := FUNCTION
  dsS := DATASET([{s}],{STRING Inp});
  R := { INTEGER2 i; };

  R Trans1(dsS le,INTEGER pos) := TRANSFORM
    SELF.i := MapChar(le.Inp[pos]) * IF ( MapChar(le.Inp[pos]) < MapChar(le.Inp[pos+1]), -1, 1 );
  END;
                
  RETURN SUM(NORMALIZE(dsS,LENGTH(TRIM(s)),Trans1(LEFT,COUNTER)),i);
END;

RomanDecode('MCMLIV');   //1954
RomanDecode('MCMXC');    //1990  
RomanDecode('MMVIII');   //2008
RomanDecode('MDCLXVI');  //1666
RomanDecode('MDLXVI');   //1566

Here's an alternative that emulates the wat procedural code would approach the problem:

IMPORT STD;
RomanDecode(STRING s) := FUNCTION
  SetWeights := [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];
  SetSymbols := ['M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I'];
  ProcessRec := RECORD
    UNSIGNED val;
    STRING Roman;
  END;
  dsSymbols := DATASET(13,TRANSFORM(ProcessRec,SELF.Roman := s, SELF := []));
	
  RECORDOF(dsSymbols) XF(dsSymbols L, dsSymbols R, INTEGER C) := TRANSFORM
    ThisRoman := IF(C=1,R.Roman,L.Roman);
    IsDone := ThisRoman = '';
    Repeatable := C IN [1,5,9,13];
    SymSize := IF(C % 2 = 0, 2, 1);  
    IsNext := STD.Str.StartsWith(ThisRoman,SetSymbols[C]);
    SymLen := IF(IsNext,
                 IF(NOT Repeatable,
                    SymSize,
                    MAP(NOT IsDone AND ThisRoman[1] = ThisRoman[2] AND ThisRoman[1] = ThisRoman[3] => 3,
                        NOT IsDone AND ThisRoman[1] = ThisRoman[2] => 2,
                        NOT IsDone  => 1,
                        0)),
                 0);

    SymbolWeight(STRING s) := IF(NOT Repeatable,
                                 SetWeights[C],
                                 CHOOSE(LENGTH(s),SetWeights[C],SetWeights[C]*2,SetWeights[C]*3,0));

    SELF.Roman := IF(IsDone,ThisRoman,ThisRoman[SymLen+1..]);
    SELF.val   := IF(IsDone,L.val,L.Val + IF(IsNext,SymbolWeight(ThisRoman[1..SymLen]),0));
  END;
  i := ITERATE(dsSymbols,XF(LEFT,RIGHT,COUNTER));
  RETURN i[13].val;
END;

RomanDecode('MCMLIV');   //1954
RomanDecode('MCMXC');    //1990  
RomanDecode('MMVIII');   //2008
RomanDecode('MDCLXVI');  //1666
RomanDecode('MDLXVI');   //1566

Eiffel

This solution is case insensitive. It performs no input validation other than checking that all Roman digits in the input string are one of M, D, C, L, X, V, and I.

class
	APPLICATION

create
	make

feature {NONE} -- Initialization

	make
		local
			numbers: ARRAY [STRING]
		do
			numbers := <<"MCMXC", "MMVIII", "MDCLXVI",
			           -- 1990     2008      1666
			              "MMMCLIX", "MCMLXXVII", "MMX">>
			           -- 3159       1977         2010
			across numbers as n loop
				print (n.item + 
                                       " in Roman numerals is " +
				       roman_to_decimal (n.item).out + 
                                       " in decimal Arabic numerals.")
				print ("%N")
			end
		end

feature -- Roman numerals

	roman_to_decimal (a_str: STRING): INTEGER
		-- Decimal representation of Roman numeral `a_str'
		require
			is_roman (a_str)
		local
			l_pos: INTEGER
			cur: INTEGER -- Value of the digit read in the current iteration
			prev: INTEGER -- Value of the digit read in the previous iteration
		do
			from
				l_pos := 0
				Result := 0
				prev := 1 + max_digit_value
			until
				l_pos = a_str.count
			loop
				l_pos := l_pos + 1
				cur := roman_digit_to_decimal (a_str.at (l_pos))
				if cur <= prev then
					-- Add nonincreasing digit
					Result := Result + cur
				else
					-- Subtract previous digit from increasing digit
					Result := Result - prev + (cur - prev)
				end
				prev := cur
			end
		ensure
			Result >= 0
		end

	is_roman (a_string: STRING): BOOLEAN
		-- Is `a_string' a valid sequence of Roman digits?
		do
			Result := across a_string as c all is_roman_digit (c.item) end
		end

feature {NONE} -- Implementation

	max_digit_value: INTEGER = 1000

	is_roman_digit (a_char: CHARACTER): BOOLEAN
		-- Is `a_char' a valid Roman digit?
		local
			l_char: CHARACTER
		do
			l_char := a_char.as_upper
			inspect l_char
				when 'I', 'V', 'X', 'L', 'C', 'D', 'M' then
					Result := True
				else
					Result := False
			end
		end

	roman_digit_to_decimal (a_char: CHARACTER): INTEGER
		-- Decimal representation of Roman digit `a_char'
		require
			is_roman_digit (a_char)
		local
			l_char: CHARACTER
		do
			l_char := a_char.as_upper
			inspect l_char
				when 'I' then
					Result := 1
				when 'V' then
					Result := 5
				when 'X' then
					Result := 10
				when 'L' then
					Result := 50
				when 'C' then
					Result := 100
				when 'D' then
					Result := 500
				when 'M' then
					Result := 1000
			end
		ensure
			Result > 0
		end

end

Elena

ELENA 6.x :

import extensions;
import system'collections;
import system'routines;
import system'culture;
 
static RomanDictionary = Dictionary.new()
                            .setAt("I".toChar(), 1)
                            .setAt("V".toChar(), 5)
                            .setAt("X".toChar(), 10)
                            .setAt("L".toChar(), 50)
                            .setAt("C".toChar(), 100)
                            .setAt("D".toChar(), 500)
                            .setAt("M".toChar(), 1000);
 
extension op : String
{
    toRomanInt()
    {
        var minus := 0;
        var s := self.toUpper();
        var total := 0;
 
        for(int i := 0; i < s.Length; i += 1)
        {
            var thisNumeral := RomanDictionary[s[i]] - minus;
            if (i >= s.Length - 1 || thisNumeral + minus >= RomanDictionary[s[i + 1]])
            {
                total += thisNumeral;
                minus := 0
            }
            else
            {
                minus := thisNumeral
            }
        };
 
        ^ total
    }
}
 
public program()
{
    console.printLine("MCMXC:  ", "MCMXC".toRomanInt());
    console.printLine("MMVIII: ", "MMVIII".toRomanInt());
    console.printLine("MDCLXVI:", "MDCLXVI".toRomanInt())
}
Output:
MCMXC:  1990
MMVIII: 2008
MDCLXVI:1666

Elixir

Translation of: Erlang
defmodule Roman_numeral do
  def decode([]), do: 0 
  def decode([x]), do: to_value(x)
  def decode([h1, h2 | rest]) do
    case {to_value(h1), to_value(h2)} do
      {v1, v2} when v1 < v2 -> v2 - v1 + decode(rest)
      {v1, _} -> v1 + decode([h2 | rest])
    end
  end
  
  defp to_value(?M), do: 1000
  defp to_value(?D), do:  500
  defp to_value(?C), do:  100
  defp to_value(?L), do:   50
  defp to_value(?X), do:   10
  defp to_value(?V), do:    5
  defp to_value(?I), do:    1
end

Enum.each(['MCMXC', 'MMVIII', 'MDCLXVI', 'IIIID'], fn clist ->
  IO.puts "#{clist}\t: #{Roman_numeral.decode(clist)}"
end)
Output:
MCMXC   : 1990
MMVIII  : 2008
MDCLXVI : 1666

Emacs Lisp

(defun ro2ar (RN)
  "Translate a roman number RN into arabic number. 
   Its argument RN is wether a symbol, wether a list. 
   Returns the arabic number. (ro2ar 'C) gives 100, 
   (ro2ar '(X X I V)) gives 24" 
  (cond 
   ((eq RN 'M) 1000)
   ((eq RN 'D) 500)
   ((eq RN 'C) 100)
   ((eq RN 'L) 50)
   ((eq RN 'X) 10)
   ((eq RN 'V) 5)
   ((eq RN 'I) 1)
   ((null (cdr RN)) (ro2ar (car RN))) ;; stop recursion
   ((< (ro2ar (car RN)) (ro2ar (car (cdr RN)))) (- (ro2ar (cdr RN)) (ro2ar (car RN)))) ;; "IV" -> 5-1=4
   (t (+ (ro2ar (car RN)) (ro2ar (cdr RN)))))) ;; "VI" -> 5+1=6
Output:
(ro2ar '(M D C L X V I)) ;=> 1666

Erlang

Putting the character X into a list, [X], creates a string with a single character.

-module( roman_numerals ).

-export( [decode_from_string/1]).

to_value($M) -> 1000;
to_value($D) ->  500;
to_value($C) ->  100;
to_value($L) ->   50;
to_value($X) ->   10;
to_value($V) ->    5;
to_value($I) ->    1.

decode_from_string([]) -> 0;
decode_from_string([H1]) -> to_value(H1);
decode_from_string([H1, H2 |Rest]) ->
    case {to_value(H1), to_value(H2)} of
        {V1, V2} when V1 < V2 -> V2 - V1 + decode_from_string(Rest);
        {V1, V1} -> V1 + V1 + decode_from_string(Rest);
        {V1, _} -> V1 + decode_from_string([H2|Rest])
    end.
Output:
10> roman_numerals:decode_from_string("MCMXC").
1990
11> roman_numerals:decode_from_string("MMVIII").
2008
12> roman_numerals:decode_from_string("MDCLXVI").
1666

ERRE

PROGRAM ROMAN2ARAB

DIM R%[7]

PROCEDURE TOARAB(ROMAN$->ANS%)
      LOCAL I%,J%,P%,N%
      FOR I%=LEN(ROMAN$) TO 1 STEP -1 DO
        J%=INSTR("IVXLCDM",MID$(ROMAN$,I%,1))
        IF J%=0 THEN
            ANS%=-9999  ! illegal character
            EXIT PROCEDURE
        END IF
        IF J%>=P% THEN
            N%+=R%[J%]
          ELSE
            N%-=R%[J%]
        END IF
        P%=J%
      END FOR
      ANS%=N%
END PROCEDURE

BEGIN
  R%[]=(0,1,5,10,50,100,500,1000)
  TOARAB("MCMXCIX"->ANS%)         PRINT(ANS%)
  TOARAB("MMXII"->ANS%)           PRINT(ANS%)
  TOARAB("MDCLXVI"->ANS%)         PRINT(ANS%)
  TOARAB("MMMDCCCLXXXVIII"->ANS%) PRINT(ANS%)
END PROGRAM

If the answer is -9999, roman number is illegal.

Euphoria

Translation of: PureBasic
constant symbols = "MDCLXVI", weights = {1000,500,100,50,10,5,1}
function romanDec(sequence roman)
    integer n, lastval, arabic
    lastval = 0
    arabic = 0
    for i = length(roman) to 1 by -1 do
        n = find(roman[i],symbols)
        if n then
            n = weights[n]
        end if
        if n < lastval then
            arabic -= n
        else
            arabic += n
        end if
        lastval = n
    end for
    return arabic
end function

? romanDec("MCMXCIX")
? romanDec("MDCLXVI")
? romanDec("XXV")
? romanDec("CMLIV")
? romanDec("MMXI")
Output:
1999
1666
25
954
2011

F#

This implementation uses tail recursion. The accumulator (arabic) and the last roman digit (lastval) are recursively passed as each element of the list is consumed.

let decimal_of_roman roman =
    let rec convert arabic lastval = function
        | head::tail ->
            let n = match head with
                    | 'M' | 'm' -> 1000
                    | 'D' | 'd' -> 500
                    | 'C' | 'c' -> 100
                    | 'L' | 'l' -> 50
                    | 'X' | 'x' -> 10
                    | 'V' | 'v' -> 5
                    | 'I' | 'i' -> 1
                    | _ -> 0
            let op = if n > lastval then (-) else (+)
            convert (op arabic lastval) n tail
        | _ -> arabic + lastval
    convert 0 0 (Seq.toList roman)
;;

Here is an alternative implementation that uses Seq(uence).fold. It threads a Tuple of the state (accumulator, last roman digit) through the list of characters.

let decimal_of_roman roman =
    let convert (arabic,lastval) c =
        let n = match c with
                | 'M' | 'm' -> 1000
                | 'D' | 'd' -> 500
                | 'C' | 'c' -> 100
                | 'L' | 'l' -> 50
                | 'X' | 'x' -> 10
                | 'V' | 'v' -> 5
                | 'I' | 'i' -> 1
                | _ -> 0
        let op = if n > lastval then (-) else (+)
        (op arabic lastval, n)
    let (arabic, lastval) = Seq.fold convert (0,0) roman
    arabic + lastval
;;

Test code:

let tests = ["MCMXC"; "MMVIII"; "MDCLXVII"; "MMMCLIX"; "MCMLXXVII"; "MMX"]
for test in tests do Printf.printf "%s: %d\n" test (decimal_of_roman test)
;;
Output:
MCMXC: 1990
MMVIII: 2008
MDCLXVII: 1667
MMMCLIX: 3159
MCMLXXVII: 1977
MMX: 2010

Factor

A roman numeral library ships with Factor.

USE: roman
( scratchpad ) "MMMCCCXXXIII" roman> .
3333

Implementation for decoding:

CONSTANT: roman-digits
    { "m" "cm" "d" "cd" "c" "xc" "l" "xl" "x" "ix" "v" "iv" "i" }

CONSTANT: roman-values
    { 1000 900 500 400 100 90 50 40 10 9 5 4 1 }

: roman> ( str -- n )
  >lower [ roman-digit>= ] monotonic-split
  [ roman-value ] map-sum ;

: roman-digit>= ( ch1 ch2 -- ? ) [ roman-digit-index ] bi@ >= ;

: roman-digit-index ( ch -- n ) 1string roman-digits index ;

: roman-value (seq -- n )
  [ [ roman-digit-value ] map ] [ all-eq? ] bi
  [ sum ] [ first2 swap - ] if ;

: roman-digit-value ( ch -- n )
  roman-digit-index roman-values nth ;

FALSE

[ 32|   {get value of Roman digit on stack}
  $'m= $[\% 1000\]? ~[
  $'d= $[\% 500\]? ~[
  $'c= $[\% 100\]? ~[
  $'l= $[\% 50\]? ~[
  $'x= $[\% 10\]? ~[
  $'v= $[\% 5\]? ~[
  $'i= $[\% 1\]? ~[
  % 0
  ]?]?]?]?]?]?]?
]r:

0           {accumulator}
^r;!        {read first Roman digit}
[^r;!$][    {read another, and as long as it is valid...}
  \$@@\$@@  {copy previous and current}
  \>[\_\]?  {if previous smaller than current, negate previous}
  @@+\      {add previous to accumulator}
]#
%+.         {add final digit to accumulator and output}
10,         {and a newline}
Output:
$ ./false -q romandec.f <<<MCMXC
1990
$ ./false -q romandec.f <<<MMVIII
2008
$ ./false -q romandec.f <<<MDCLXVI
1666
$ ./false -q romandec.f <<<MMXXI
2021

Forth

create (arabic)
  1000 128 * char M + ,
   500 128 * char D + ,
   100 128 * char C + ,
    50 128 * char L + ,
    10 128 * char X + ,
     5 128 * char V + ,
     1 128 * char I + ,
does>
  7 cells bounds do
    i @ over over 127 and = if nip 7 rshift leave else drop then
  1 cells +loop dup
;

: >arabic
  0 dup >r >r
  begin
    over over
  while
    c@ dup (arabic) rot <>
  while
    r> over r> over over > if 2* negate + else drop then + swap >r >r 1 /string
  repeat then drop 2drop r> r> drop
;

s" MCMLXXXIV" >arabic .


\ decode roman numerals using Forth methodology
\ create words to describe and solve the problem
\ ANS/ISO Forth

\ state holders
VARIABLE OLDNDX
VARIABLE CURNDX
VARIABLE NEGFLAG

DECIMAL
CREATE VALUES ( -- addr) 0 , 1 , 5 , 10 , 50 , 100 , 500 , 1000 ,

: NUMERALS ( -- addr len)  S"  IVXLCDM" ;        \ 1st char is a blank
: []       ( n addr -- addr') SWAP CELLS +  ;    \ array address calc.
: INIT     ( -- )         CURNDX OFF  OLDNDX OFF  NEGFLAG OFF ;
: REMEMBER ( ndx -- ndx ) CURNDX @ OLDNDX !  DUP CURNDX !  ;
: ]VALUE@  ( ndx -- n )   REMEMBER VALUES [] @ ;
HEX
: TOUPPER ( char -- char ) 05F AND ;

DECIMAL
: >INDEX   ( char -- ndx) TOUPPER >R  NUMERALS TUCK R> SCAN NIP -
                          DUP 7 > ABORT" Invalid Roman numeral" ;

: >VALUE   ( char -- n ) >INDEX ]VALUE@ ;
: ?ILLEGAL ( ndx --  )   CURNDX @ OLDNDX @ =  NEGFLAG @ AND ABORT" Illegal format" ;

: ?NEGATE ( n -- +n | -n) \ conditional NEGATE
           CURNDX @ OLDNDX @ <
           IF   NEGFLAG ON  NEGATE
           ELSE ?ILLEGAL  NEGFLAG OFF
           THEN ;

: >ARABIC  ( addr len -- n )
           INIT
           0  -ROT            \ accumulator under the stack string args
           1- BOUNDS          \ convert addr len to two addresses 
           SWAP DO            \ index the string from back to front
                  I C@ >VALUE ?NEGATE +    
          -1 +LOOP ;

Alternative Version Forth Console Test

\ test code  ok
S" i"         >ARABIC . 1  ok
S" ii"        >ARABIC . 2  ok
S" iv"        >ARABIC . 4  ok
S" mdclxvi"   >ARABIC . 1666  ok
S" mm"        >ARABIC . 2000  ok
S" mmm"       >ARABIC . 3000  ok
S" MCMLIV"    >ARABIC . 1954  ok
S" mcmxlvi"   >ARABIC . 1946  ok
S" mdc"       >ARABIC . 1600  ok
S" mdcl"      >ARABIC . 1650  ok
S" mdclxvi"   >ARABIC . 1666  ok
S" mcmlxxxiv" >ARABIC . 1984  ok

Fortran

Works with: Fortran version 90 and later
program Roman_decode
  implicit none
 
  write(*,*) decode("MCMXC"), decode("MMVIII"), decode("MDCLXVI")

contains

function decode(roman) result(arabic)
  character(*), intent(in) :: roman
  integer :: i, n, lastval, arabic

  arabic = 0
  lastval = 0
  do i = len(roman), 1, -1
    select case(roman(i:i))
      case ('M','m')
        n = 1000
      case ('D','d')
        n = 500
      case ('C','c')
        n = 100
      case ('L','l')
        n = 50
      case ('X','x')
        n = 10
      case ('V','v')
        n = 5
      case ('I','i')
        n = 1
      case default
        n = 0
    end select
    if (n < lastval) then
      arabic = arabic - n
    else
      arabic = arabic + n
    end if
    lastval = n
  end do
end function decode
end program Roman_decode
Output:
        1990        2008        1666

Go

For fluff, the unicode overbar is recognized as a factor of 1000, as described in WP.

package main

import (
    "errors"
    "fmt"
)

var m = map[rune]int{
    'I': 1,
    'V': 5,
    'X': 10,
    'L': 50,
    'C': 100,
    'D': 500,
    'M': 1000,
}

func parseRoman(s string) (r int, err error) {
    if s == "" {
        return 0, errors.New("Empty string")
    }
    is := []rune(s) // easier to convert string up front
    var c0 rune     // c0: roman character last read
    var cv0 int     // cv0: value of cv

    // the key to the algorithm is to process digits from right to left
    for i := len(is) - 1; i >= 0; i-- {
        // read roman digit
        c := is[i]
        k := c == '\u0305' // unicode overbar combining character
        if k {
            if i == 0 {
                return 0, errors.New(
                    "Overbar combining character invalid at position 0")
            }
            i--
            c = is[i]
        }
        cv := m[c]
        if cv == 0 {
            if c == 0x0305 {
                return 0, fmt.Errorf(
                    "Overbar combining character invalid at position %d", i)
            } else {
                return 0, fmt.Errorf(
                    "Character unrecognized as Roman digit: %c", c)
            }
        }
        if k {
            c = -c // convention indicating overbar
            cv *= 1000
        }

        // handle cases of new, same, subtractive, changed, in that order.
        switch {
        default: // case 4: digit change
            fallthrough
        case c0 == 0: // case 1: no previous digit
            c0 = c
            cv0 = cv
        case c == c0: // case 2: same digit
        case cv*5 == cv0 || cv*10 == cv0: // case 3: subtractive
            // handle next digit as new.
            // a subtractive digit doesn't count as a previous digit.
            c0 = 0
            r -= cv  // subtract...
            continue // ...instead of adding
        }
        r += cv // add, in all cases except subtractive
    }
    return r, nil
}

func main() {
    // parse three numbers mentioned in task description
    for _, r := range []string{"MCMXC", "MMVIII", "MDCLXVI"} {
        v, err := parseRoman(r)
        if err != nil {
            fmt.Println(err)
        } else {
            fmt.Println(r, "==", v)
        }
    }
}
Output:
MCMXC == 1990
MMVIII == 2008
MDCLXVI == 1666

Simpler:

package main

import (
	"fmt"
)

var m = map[rune]int{
	'I': 1,
	'V': 5,
	'X': 10,
	'L': 50,
	'C': 100,
	'D': 500,
	'M': 1000,
}

// function, per task description
func from_roman(roman string) (arabic int) {
	last_digit := 1000
	for _, r := range roman {
		digit := m[r]
		if last_digit < digit {
			arabic -= 2 * last_digit
		}
		last_digit = digit
		arabic += digit
	}

	return arabic
}

func main() {
	// parse three numbers mentioned in task description
	for _, roman_digit := range []string{"MCMXC", "MMVIII", "MDCLXVI"} {
		fmt.Printf("%-10s == %d\n", roman_digit, from_roman(roman_digit))
	}
}

Golo

#!/usr/bin/env golosh
----
This module converts a Roman numeral into a decimal number.
----
module Romannumeralsdecode

augment java.lang.Character {

  function decode = |this| -> match {
    when this == 'I' then 1
    when this == 'V' then 5
    when this == 'X' then 10
    when this == 'L' then 50
    when this == 'C' then 100
    when this == 'D' then 500
    when this == 'M' then 1000
    otherwise 0
  }
}

augment java.lang.String {

  function decode = |this| {
    var accumulator = 0
    foreach i in [0..this: length()] {
      let currentChar = this: charAt(i)
      let nextChar = match {
        when i + 1 < this: length() then this: charAt(i + 1)
        otherwise null
      }
      if (currentChar: decode() < (nextChar?: decode() orIfNull 0)) {
        # if this is something like IV or IX or whatever
        accumulator = accumulator - currentChar: decode()
      } else {
        accumulator = accumulator + currentChar: decode()
      }
    }
    return accumulator
  }
}

function main = |args| {
  println("MCMXC = " + "MCMXC": decode())
  println("MMVIII = " + "MMVIII": decode())
  println("MDCLXVI = " + "MDCLXVI": decode())
}

Groovy

Solution:

enum RomanDigits {
    I(1), V(5), X(10), L(50), C(100), D(500), M(1000);
    
    private magnitude;
    private RomanDigits(magnitude) { this.magnitude = magnitude }
    
    String toString() { super.toString() + "=${magnitude}" }
    
    static BigInteger parse(String numeral) {
        assert numeral != null && !numeral.empty
        def digits = (numeral as List).collect {
            RomanDigits.valueOf(it)
        }
        def L = digits.size()
        (0..<L).inject(0g) { total, i ->
            def sign = (i == L - 1 || digits[i] >= digits[i+1]) ? 1 : -1
            total + sign * digits[i].magnitude
        }
    }
}

Test:

println """
Digit Values = ${RomanDigits.values()}
M       => ${RomanDigits.parse('M')}
MCXI    => ${RomanDigits.parse('MCXI')}
CMXI    => ${RomanDigits.parse('CMXI')}
MCM     => ${RomanDigits.parse('MCM')}
MCMXC   => ${RomanDigits.parse('MCMXC')}
MMVIII  => ${RomanDigits.parse('MMVIII')}
MMIX    => ${RomanDigits.parse('MMIX')}
MCDXLIV => ${RomanDigits.parse('MCDXLIV')}
MDCLXVI => ${RomanDigits.parse('MDCLXVI')}
"""
Output:
Digit Values = [I=1, V=5, X=10, L=50, C=100, D=500, M=1000]
M       => 1000
MCXI    => 1111
CMXI    => 911
MCM     => 1900
MCMXC   => 1990
MMVIII  => 2008
MMIX    => 2009
MCDXLIV => 1444
MDCLXVI => 1666

Haskell

Simple declarative idiom

Compiles with GHC.

module Main where

------------------------
--  DECODER FUNCTION  --
------------------------

decodeDigit :: Char -> Int
decodeDigit 'I' = 1
decodeDigit 'V' = 5
decodeDigit 'X' = 10
decodeDigit 'L' = 50
decodeDigit 'C' = 100
decodeDigit 'D' = 500
decodeDigit 'M' = 1000
decodeDigit _ = error "invalid digit"

--  We process a Roman numeral from right to left, digit by digit, adding the value.
--  If a digit is lower than the previous then its value is negative.
--  The first digit is always positive.

decode roman = decodeRoman startValue startValue rest
  where
    (first:rest) = reverse roman
    startValue = decodeDigit first

decodeRoman :: Int -> Int -> [Char] -> Int
decodeRoman lastSum _ [] = lastSum
decodeRoman lastSum lastValue (digit:rest) = decodeRoman updatedSum digitValue rest
  where
    digitValue = decodeDigit digit
    updatedSum = (if digitValue < lastValue then (-) else (+)) lastSum digitValue

------------------
--  TEST SUITE  --
------------------

main = do
  test "MCMXC" 1990
  test "MMVIII" 2008
  test "MDCLXVI" 1666

test roman expected = putStrLn (roman ++ " = " ++ (show (arabic)) ++ remark)
  where
    arabic = decode roman
    remark = " (" ++ (if arabic == expected then "PASS" else ("FAIL, expected " ++ (show expected))) ++ ")"
Output:
MCMXC = 1990 (PASS)
MMVIII = 2008 (PASS)
MDCLXVI = 1666 (PASS)

Same logic as above but in a functional idiom

module Main where

------------------------
--  DECODER FUNCTION  --
------------------------

decodeDigit :: Char -> Int
decodeDigit 'I' = 1
decodeDigit 'V' = 5
decodeDigit 'X' = 10
decodeDigit 'L' = 50
decodeDigit 'C' = 100
decodeDigit 'D' = 500
decodeDigit 'M' = 1000
decodeDigit _ = error "invalid digit"

--  We process a Roman numeral from right to left, digit by digit, adding the value.
--  If a digit is lower than the previous then its value is negative.
--  The first digit is always positive.

decode roman = fst (foldl addValue (0, 0) (reverse roman))
  where
    addValue (lastSum, lastValue) digit = (updatedSum, value)
      where
        value = decodeDigit digit;
        updatedSum = (if value < lastValue then (-) else (+)) lastSum value

------------------
--  TEST SUITE  --
------------------

main = do
  test "MCMXC" 1990
  test "MMVIII" 2008
  test "MDCLXVI" 1666

test roman expected = putStrLn (roman ++ " = " ++ (show (arabic)) ++ remark)
  where
    arabic = decode roman
    remark = " (" ++ (if arabic == expected then "PASS" else ("FAIL, expected " ++ (show expected))) ++ ")"

List comprehension

import Data.List (isPrefixOf)

mapping = [("M",1000),("CM",900),("D",500),("CD",400),("C",100),("XC",90),
           ("L",50),("XL",40),("X",10),("IX",9),("V",5),("IV",4),("I",1)]

toArabic :: String -> Int
toArabic "" = 0
toArabic str = num + toArabic xs
    where (num, xs):_ = [ (num, drop (length n) str) | (n,num) <- mapping, isPrefixOf n str ]

Usage:

ghci> toArabic "MCMXC"
1990
ghci> toArabic "MMVIII"
2008
ghci> toArabic "MDCLXVI"
1666

mapAccum

Or, expressing romanValue in terms of mapAccumL (avoiding recursive descent, and visiting each k v pair just once)

import Data.Bifunctor (bimap)
import Data.List (isPrefixOf, mapAccumL)

romanValue :: String -> Int
romanValue =
  let tr s (k, v) =
        until
          (not . isPrefixOf k . fst)
          (bimap ((drop . length) k) (v +))
          (s, 0)
   in sum
        . snd
        . flip
          (mapAccumL tr)
          [ ("M", 1000),
            ("CM", 900),
            ("D", 500),
            ("CD", 400),
            ("C", 100),
            ("XC", 90),
            ("L", 50),
            ("XL", 40),
            ("X", 10),
            ("IX", 9),
            ("V", 5),
            ("IV", 4),
            ("I", 1)
          ]

main :: IO ()
main =
  mapM_
    (print . romanValue)
    [ "MDCLXVI",
      "MCMXC",
      "MMVIII",
      "MMXVI",
      "MMXVII"
    ]

Or, in a mapAccumR version:

import Data.List (mapAccumR)
import qualified Data.Map.Strict as M
import Data.Maybe (maybe)

fromRoman :: String -> Maybe Int
fromRoman cs =
  let go l r
        | l > r = (- r, l)
        | otherwise = (r, l)
   in traverse (`M.lookup` mapRoman) cs
        >>= ( Just . sum . ((:) <$> fst <*> snd)
                . mapAccumR go 0
            )

mapRoman :: M.Map Char Int
mapRoman =
  M.fromList $
    zip
      "MDCLXVI "
      [ 1000,
        500,
        100,
        50,
        10,
        5,
        1,
        0
      ]

--------------------------- TEST -------------------------
main :: IO ()
main =
  putStrLn $
    fTable
      "Decoding Roman numbers:\n"
      show
      (maybe "Unrecognised character" show)
      fromRoman
      [ "MDCLXVI",
        "MCMXC",
        "MMVIII",
        "MMXVI",
        "MMXVIII",
        "MMXBIII"
      ]

------------------------ FORMATTING ----------------------
fTable ::
  String ->
  (a -> String) ->
  (b -> String) ->
  (a -> b) ->
  [a] ->
  String
fTable s xShow fxShow f xs =
  unlines $
    s :
    fmap
      ( ((<>) . rjust w ' ' . xShow)
          <*> ((" -> " <>) . fxShow . f)
      )
      xs
  where
    rjust n c = drop . length <*> (replicate n c <>)
    w = maximum (length . xShow <$> xs)
Output:
Decoding Roman numbers:

"MDCLXVI" -> 1666
  "MCMXC" -> 1990
 "MMVIII" -> 2008
  "MMXVI" -> 2016
"MMXVIII" -> 2018
"MMXBIII" -> Unrecognised character

Fold

An alternative solution using a fold. (This turns out to be the fastest of the four approaches here)

Translation of: F#
import qualified Data.Map.Strict as M

fromRoman :: String -> Int
fromRoman xs = partialSum + lastDigit
  where
    (partialSum, lastDigit) = foldl accumulate (0, 0) (evalRomanDigit <$> xs)
    accumulate (partial, lastDigit) newDigit
      | newDigit <= lastDigit = (partial + lastDigit, newDigit)
      | otherwise = (partial - lastDigit, newDigit)

mapRoman :: M.Map Char Int
mapRoman =
  M.fromList
    [ ('I', 1)
    , ('V', 5)
    , ('X', 10)
    , ('L', 50)
    , ('C', 100)
    , ('D', 500)
    , ('M', 1000)
    ]

evalRomanDigit :: Char -> Int
evalRomanDigit c =
  let mInt = M.lookup c mapRoman
  in case mInt of
       Just x -> x
       _ -> error $ c : " is not a roman digit"

main :: IO ()
main = print $ fromRoman <$> ["MDCLXVI", "MCMXC", "MMVIII", "MMXVI", "MMXVII"]


Where the left fold above could also be rewritten | as a right fold.

import qualified Data.Map.Strict as M
import Data.Maybe (maybe)

------------------ ROMAN NUMERALS DECODED ----------------

mapRoman :: M.Map Char Int
mapRoman =
  M.fromList $
    zip "IVXLCDM" $
      scanl (*) 1 (cycle [5, 2])

fromRoman :: String -> Maybe Int
fromRoman cs =
  let op l r
        | l >= r = (+)
        | otherwise = (-)
   in snd
        . foldr
          (\l (r, n) -> (l, op l r n l))
          (0, 0)
        <$> traverse (`M.lookup` mapRoman) cs

--------------------------- TEST -------------------------
main :: IO ()
main =
  putStrLn $
    fTable
      "Roman numeral decoding as a right fold:\n"
      show
      (maybe "(Unrecognised character seen)" show)
      fromRoman
      [ "MDCLXVI",
        "MCMXC",
        "MMVIII",
        "MMXVI",
        "MMXVII",
        "QQXVII"
      ]

------------------------ FORMATTING ----------------------

fTable ::
  String ->
  (a -> String) ->
  (b -> String) ->
  (a -> b) ->
  [a] ->
  String
fTable s xShow fxShow f xs =
  unlines $
    s :
    fmap
      ( ((<>) . rjust w ' ' . xShow)
          <*> ((" -> " <>) . fxShow . f)
      )
      xs
  where
    rjust n c = drop . length <*> (replicate n c <>)
    w = maximum (length . xShow <$> xs)
Output:
Roman numeral decoding as a right fold:

"MDCLXVI" -> 1666
  "MCMXC" -> 1990
 "MMVIII" -> 2008
  "MMXVI" -> 2016
 "MMXVII" -> 2017
 "QQXVII" -> (Unrecognised character seen)

sum . catMaybes

Summing a list of Map.lookup results on indexed [Char, Char] pairs.

(Probably more trouble than it's worth in practice, but at least an illustration of some Data.Maybe and Data.Map functions)

import qualified Data.Map.Strict as M (Map, fromList, lookup)
import Data.Maybe (isNothing, isJust, fromJust, catMaybes)
import Data.List (mapAccumL)
 
mapRoman :: M.Map String Int
mapRoman =
  M.fromList
    [ ("M", 1000)
    , ("CM", 900)
    , ("D", 500)
    , ("CD", 400)
    , ("C", 100)
    , ("XC", 90)
    , ("L", 50)
    , ("XL", 40)
    , ("X", 10)
    , ("IX", 9)
    , ("V", 5)
    , ("IV", 4)
    , ("I", 1)
    ]
 
fromRoman :: String -> Int
fromRoman s =
  let value k = M.lookup k mapRoman
  in sum . catMaybes . snd $
     mapAccumL
       (\mi (l, r, i) ->
           let mValue = value [l, r] -- mapRoman lookup of [left, right] Chars
               (lastPair, pairValue)
                 | isJust mValue = (Just i, mValue) -- Pair match: index updated
                 | isNothing mi || i - fromJust mi > 1 = (mi, value [l])
                 | otherwise = (mi, Nothing) -- Left Char was counted in pair
           in (lastPair, pairValue))
       Nothing -- Accumulator – maybe Index to last matched Char pair
       (zip3 s (tail s ++ " ") [0 ..]) -- Indexed character pairs
 
main :: IO ()
main = print $ fromRoman <$> ["MDCLXVI", "MCMXC", "MMVIII", "MMXVI", "MMXVII"]
Output:
[1666,1990,2008,2016,2017]

Hoon

Library file (e.g. /lib/rhonda.hoon):

|%
++  parse
  |=  t=tape  ^-  @ud
  =.  t  (cass t)
  =|  result=@ud
  |-
  ?~  t  result
  ?~  t.t  (add result (from-numeral i.t))
  =+  [a=(from-numeral i.t) b=(from-numeral i.t.t)]
  ?:  (gte a b)  $(result (add result a), t t.t)
  $(result (sub (add result b) a), t t.t.t)
++  yield
  |=  n=@ud  ^-  tape
  =|  result=tape
  =/  values  to-numeral
  |-
  ?~  values  result
  ?:  (gte n -.i.values)
    $(result (weld result +.i.values), n (sub n -.i.values))
  $(values t.values)
++  from-numeral
  |=  c=@t  ^-  @ud
  ?:  =(c 'i')  1
  ?:  =(c 'v')  5
  ?:  =(c 'x')  10
  ?:  =(c 'l')  50
  ?:  =(c 'c')  100
  ?:  =(c 'd')  500
  ?:  =(c 'm')  1.000
  !!
++  to-numeral
  ^-  (list [@ud tape])
  :*
    [1.000 "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"]
    ~
  ==
--

Script file ("generator") (e.g. /gen/roman.hoon):

/+  *roman
:-  %say
|=  [* [x=$%([%from-roman tape] [%to-roman @ud]) ~] ~]
:-  %noun
^-  tape
?-  -.x
  %from-roman  "{<(parse +.x)>}"
  %to-roman  (yield +.x)
==

Icon and Unicon

link numbers

procedure main()
every R := "MCMXC"|"MDCLXVI"|"MMVIII" do 
   write(R, " = ",unroman(R))
end

numbers.icn provides unroman

The code for this procedure is copied below:

procedure unroman(s)		#: convert Roman numeral to integer
   local nbr,lastVal,val

   nbr := lastVal := 0
   s ? {
      while val := case map(move(1)) of {
	 "m": 1000
	 "d": 500
	 "c": 100
	 "l": 50
	 "x": 10
	 "v": 5
	 "i": 1
	 } do {
	 nbr +:= if val <= lastVal then val else val - 2 * lastVal
	 lastVal := val
	 }
      }
   return nbr
end
Output:
MCMXC = 1990
MDCLXVI = 1666
MMVIII = 2008

Insitux

Translation of: Clojure
(var numerals {"M" 1000 "D" 500 "C" 100 "L" 50 "X" 10 "V" 5 "I" 1})

; Approach A
(function ro->ar r
  (-> (reverse (upper-case r))
      (map numerals)
      (split-with val)
      (map (.. +0))
      (reduce @(((< % %1) + -)))))

; Approach B
(function ro->ar r 
  (-> (upper-case r) 
      (map numerals) 
     @(reduce (fn [sum lastv] curr [(+ sum curr ((< lastv curr) (* -2 lastv) 0)) curr]) [0 0]) 
      0))

(map ro->ar ["MDCLXVI" "MMMCMXCIX" "XLVIII" "MMVIII"])
Output:
[1666 3999 48 2008]

J

rom2d=: [: (+/ .*  _1^ 0,~ 2</\ ]) 1 5 10 50 100 500 1000 {~ 'IVXLCDM'&i.

Example use:

   rom2d 'MCMXC'
1990
   rom2d 'MDCLXVI'
1666
   rom2d 'MMVIII'
2008

Java

Works with: Java version 1.5+
public class Roman {
	private static int decodeSingle(char letter) {
		switch(letter) {
			case 'M': return 1000;
			case 'D': return 500;
			case 'C': return 100;
			case 'L': return 50;
			case 'X': return 10;
			case 'V': return 5;
			case 'I': return 1;
			default: return 0;
		}
	}
	public static int decode(String roman) {
		int result = 0;
		String uRoman = roman.toUpperCase(); //case-insensitive
		for(int i = 0;i < uRoman.length() - 1;i++) {//loop over all but the last character
			//if this character has a lower value than the next character
			if (decodeSingle(uRoman.charAt(i)) < decodeSingle(uRoman.charAt(i+1))) {
				//subtract it
				result -= decodeSingle(uRoman.charAt(i));
			} else {
				//add it
				result += decodeSingle(uRoman.charAt(i));
			}
		}
		//decode the last character, which is always added
		result += decodeSingle(uRoman.charAt(uRoman.length()-1));
		return result;
	}
	
	public static void main(String[] args) {
		System.out.println(decode("MCMXC")); //1990
		System.out.println(decode("MMVIII")); //2008
		System.out.println(decode("MDCLXVI")); //1666
	}
}
Output:
1990
2008
1666
Works with: Java version 1.8+
import java.util.Set;
import java.util.EnumSet;
import java.util.Collections;
import java.util.stream.Collectors;
import java.util.stream.LongStream;

public interface RomanNumerals {
  public enum Numeral {
    M(1000), CM(900), D(500), CD(400), C(100), XC(90), L(50), XL(40), X(10), IX(9), V(5), IV(4), I(1);

    public final long weight;

    private static final Set<Numeral> SET = Collections.unmodifiableSet(EnumSet.allOf(Numeral.class));

    private Numeral(long weight) {
      this.weight = weight;
    }

    public static Numeral getLargest(long weight) {
      return SET.stream()
        .filter(numeral -> weight >= numeral.weight)
        .findFirst()
        .orElse(I)
      ;
    }
  };

  public static String encode(long n) {
    return LongStream.iterate(n, l -> l - Numeral.getLargest(l).weight)
      .limit(Numeral.values().length)
      .filter(l -> l > 0)
      .mapToObj(Numeral::getLargest)
      .map(String::valueOf)
      .collect(Collectors.joining())
    ;
  }

  public static long decode(String roman) {
    long result =  new StringBuilder(roman.toUpperCase()).reverse().chars()
      .mapToObj(c -> Character.toString((char) c))
      .map(numeral -> Enum.valueOf(Numeral.class, numeral))
      .mapToLong(numeral -> numeral.weight)
      .reduce(0, (a, b) -> a + (a <= b ? b : -b))
    ;
    if (roman.charAt(0) == roman.charAt(1)) {
      result += 2 * Enum.valueOf(Numeral.class, roman.substring(0, 1)).weight;
    }
    return result;
  }

  public static void test(long n) {
    System.out.println(n + " = " + encode(n));
    System.out.println(encode(n) + " = " + decode(encode(n)));
  }

  public static void main(String[] args) {
    LongStream.of(1999, 25, 944).forEach(RomanNumerals::test);
  }
}
Output:
1999 = MCMXCIX
MCMXCIX = 1999
25 = XXV
XXV = 25
944 = CMXLIV
CMXLIV = 944

JavaScript

ES5

Imperative

Works with: Rhino
Works with: SpiderMonkey
var Roman = {
  Values: [['CM', 900],  ['CD', 400], ['XC',  90], ['XL',  40], ['IV', 4],   
           ['IX',   9], ['V',   5], ['X',   10], ['L',  50], 
           ['C',  100], ['M', 1000], ['I',    1], ['D',  500]],
  UnmappedStr : 'Q',
  parse: function(str) {
    var result = 0
    for (var i=0; i<Roman.Values.length; ++i) {
      var pair = Roman.Values[i]
      var key = pair[0]
      var value = pair[1]
      var regex = RegExp(key)
      while (str.match(regex)) {
        result += value
        str = str.replace(regex, Roman.UnmappedStr)
      }
    }
    return result
  }
}

var test_data = ['MCMXC', 'MDCLXVI', 'MMVIII']
for (var i=0; i<test_data.length; ++i) {
  var test_datum = test_data[i]
  print(test_datum + ": " + Roman.parse(test_datum)) 
}
Output:
MCMXC: 1990
MDCLXVI: 1666
MMVIII: 2008

Functional

Translation of: Haskell

(isPrefixOf example)

(function (lstTest) {
 
    var mapping = [["M", 1000], ["CM", 900], ["D", 500], ["CD", 400], ["C", 100], [
        "XC", 90], ["L", 50], ["XL", 40], ["X", 10], ["IX", 9], ["V", 5], ["IV",
        4], ["I", 1]];
 
    // s -> n
    function romanValue(s) {
        // recursion over list of characters
        // [c] -> n
        function toArabic(lst) {
            return lst.length ? function (xs) {
                var lstParse = chain(mapping, function (lstPair) {
                    return isPrefixOf(
                        lstPair[0], xs
                    ) ? [lstPair[1], drop(lstPair[0].length, xs)] : []
                });
                return lstParse[0] + toArabic(lstParse[1]);
            }(lst) : 0
        }
        return toArabic(s.split(''));
    }
 
    // Monadic bind (chain) for lists
    function chain(xs, f) {
        return [].concat.apply([], xs.map(f));
    }
 
    // [a] -> [a] -> Bool
    function isPrefixOf(lstFirst, lstSecond) {
        return lstFirst.length ? (
            lstSecond.length ?
            lstFirst[0] === lstSecond[0] && isPrefixOf(
                lstFirst.slice(1), lstSecond.slice(1)
            ) : false
        ) : true;
    }
 
    // Int -> [a] -> [a]
    function drop(n, lst) {
        return n <= 0 ? lst : (
            lst.length ? drop(n - 1, lst.slice(1)) : []
        );
    }
 
    return lstTest.map(romanValue);
 
})(['MCMXC', 'MDCLXVI', 'MMVIII']);
Output:
[1990, 1666, 2008]

or, more natively:

(function (lstTest) {
 
    function romanValue(s) {
        return s.length ? function () {
            var parse = [].concat.apply([], glyphs.map(function (g) {
                return 0 === s.indexOf(g) ? [trans[g], s.substr(g.length)] : [];
            }));
            return parse[0] + romanValue(parse[1]);
        }() : 0;
    }
 
    var trans = {
            M: 1E3,
            CM: 900,
            D: 500,
            CD: 400,
            C: 100,
            XC: 90,
            L: 50,
            XL: 40,
            X: 10,
            IX: 9,
            V: 5,
            IV: 4,
            I: 1
        },
        glyphs = Object.keys(trans);
 
    return lstTest.map(romanValue);
 
})(["MCMXC", "MDCLXVI", "MMVIII", "MMMM"]);
Output:
[1990, 1666, 2008]

ES6

Recursion

(() => {
    // romanValue :: String -> Int
    const romanValue = s =>
        s.length ? (() => {
            const parse = [].concat(
                ...glyphs.map(g => 0 === s.indexOf(g) ? (
                    [dctTrans[g], s.substr(g.length)]
                ) : [])
            );
            return parse[0] + romanValue(parse[1]);
        })() : 0;

    // dctTrans :: {romanKey: Integer}
    const dctTrans = {
        M: 1E3,
        CM: 900,
        D: 500,
        CD: 400,
        C: 100,
        XC: 90,
        L: 50,
        XL: 40,
        X: 10,
        IX: 9,
        V: 5,
        IV: 4,
        I: 1
    };

    // glyphs :: [romanKey]
    const glyphs = Object.keys(dctTrans);

    // TEST -------------------------------------------------------------------
    return ["MCMXC", "MDCLXVI", "MMVIII", "MMMM"].map(romanValue);
})();
Output:
[1990,1666,2008,4000]


Folding from the right

Translation of: Haskell

(fold and foldr examples)

(() => {

    // -------------- ROMAN NUMERALS DECODED ---------------

    // Folding from right to left,
    // lower leftward characters are subtracted,
    // others are added.

    // fromRoman :: String -> Int
    const fromRoman = s =>
        foldr(l => ([r, n]) => [
            l,
            l >= r ? (
                n + l
            ) : n - l
        ])([0, 0])(
            [...s].map(charVal)
        )[1];

    // charVal :: Char -> Maybe Int
    const charVal = k => {
        const v = {
            I: 1,
            V: 5,
            X: 10,
            L: 50,
            C: 100,
            D: 500,
            M: 1000
        } [k];
        return v !== undefined ? v : 0;
    };

    // ----------------------- TEST ------------------------
    const main = () => [
            'MDCLXVI', 'MCMXC', 'MMVIII', 'MMXVI', 'MMXVII'
        ]
        .map(fromRoman)
        .join('\n');


    // ----------------- GENERIC FUNCTIONS -----------------

    // foldr :: (a -> b -> b) -> b -> [a] -> b
    const foldr = f =>
        // Note that that the Haskell signature of foldr 
        // differs from that of foldl - the positions of 
        // accumulator and current value are reversed.
        a => xs => [...xs].reduceRight(
            (a, x) => f(x)(a),
            a
        );

    // MAIN ---
    return main();
})();
Output:
1666
1990
2008
2016
2017

Declarative

(() => {
function toNumeric(value) {
    return value
        .replace(/IV/, 'I'.repeat(4))
        .replace(/V/g, 'I'.repeat(5))
        .replace(/IX/, 'I'.repeat(9))
        .replace(/XC/, 'I'.repeat(90))
        .replace(/XL/, 'I'.repeat(40))
        .replace(/X/g, 'I'.repeat(10))
        .replace(/L/, 'I'.repeat(50))
        .replace(/CD/, 'I'.repeat(400))
        .replace(/CM/, 'I'.repeat(900))
        .replace(/C/g, 'I'.repeat(100))
        .replace(/D/g, 'I'.repeat(500))
        .replace(/M/g, 'I'.repeat(1000))
        .length;
}

const numerics = ["MMXVI", "MCMXC", "MMVIII", "MM", "MDCLXVI"]
    .map(toNumeric);

console.log(numerics);
})();
Output:
[2016, 1990, 2008, 2000, 1666]

jq

Works with: jq version 1.4

This version requires the Roman numerals to be presented in upper case.

def fromRoman:
  def addRoman(n):
    if length == 0 then n
    elif startswith("M")  then .[1:] | addRoman(1000 + n)
    elif startswith("CM") then .[2:] | addRoman(900 + n)
    elif startswith("D")  then .[1:] | addRoman(500 + n)
    elif startswith("CD") then .[2:] | addRoman(400 + n)
    elif startswith("C")  then .[1:] | addRoman(100 + n)
    elif startswith("XC") then .[2:] | addRoman(90 + n)
    elif startswith("L")  then .[1:] | addRoman(50 + n)
    elif startswith("XL") then .[2:] | addRoman(40 + n)
    elif startswith("X")  then .[1:] | addRoman(10 + n)
    elif startswith("IX") then .[2:] | addRoman(9 + n)
    elif startswith("V")  then .[1:] | addRoman(5 + n)
    elif startswith("IV") then .[2:] | addRoman(4 + n)
    elif startswith("I")  then .[1:] | addRoman(1 + n)
    else
      error("invalid Roman numeral: " + tostring)
    end;
  addRoman(0);

Example:

[ "MCMXC", "MMVIII", "MDCLXVI" ] | map("\(.) => \(fromRoman)") | .[]
Output:
$ jq -n -f -r fromRoman.jq
MCMXC => 1990
MMVIII => 2008
MDCLXVI => 1666

Jsish

Duplicate of the Jsish module used in Roman_numerals/Encode#Jsish.

Output:
prompt$ jsish -e 'require("Roman"); puts(Roman.fromRoman("MDCLXVI"));'
1666

Julia

Works with: Julia version 0.6

The Function:

function parseroman(rnum::AbstractString)
    romandigits = Dict('I' => 1, 'V' => 5, 'X' => 10, 'L' => 50,
                       'C' => 100, 'D' => 500, 'M' => 1000)
    mval = accm = 0
    for d in reverse(uppercase(rnum))
        val = try
            romandigits[d]
        catch
            throw(DomainError())
        end
        if val > mval maxval = val end
        if val < mval
            accm -= val
        else
            accm += val
        end
    end
    return accm
end

This function is rather permissive. There are no limitations on the numbers of Roman numerals nor on their order. Because of this and because any out of order numerals subtract from the total represented, it is possible to represent zero and negative integers. Also mixed case representations are allowed. The function does throw an error if the string contains any invalid characters.

Test the code:

using Printf

test = ["I", "III", "IX", "IVI", "IIM",
        "CMMDXL", "icv", "cDxLiV", "MCMLD", "ccccccd",
        "iiiiiv", "MMXV", "MCMLXXXIV", "ivxmm", "SPQR"]
for rnum in test
    @printf("%15s%s\n", rnum, try parseroman(rnum) catch "not valid" end)
end
Output:
              I → 1
            III → 3
             IX → 11
            IVI → 7
            IIM → 1002
         CMMDXL → 2660
            icv → 106
         cDxLiV → 666
          MCMLD → 2650
        ccccccd → 1100
         iiiiiv → 10
           MMXV → 2015
      MCMLXXXIV → 2186
          ivxmm → 2016
           SPQR → not valid

K

Translation of: J
  romd: {v:1 5 10 50 100 500 1000@"IVXLCDM"?/:x; +/v*_-1^(>':v),0}

Example:

  romd'("MCMXC";"MMVIII";"MDCLXVI")
1990 2008 1666

Kotlin

As specified in the task description, there is no attempt to validate the form of the Roman number in the following program - invalid characters and ordering are simply ignored:

// version 1.0.6

fun romanDecode(roman: String): Int {
    if (roman.isEmpty()) return 0
    var n = 0
    var last = 'O'
    for (c in roman) {        
        when (c) {
            'I' -> n += 1   
            'V' -> if (last == 'I') n += 3   else n += 5
            'X' -> if (last == 'I') n += 8   else n += 10
            'L' -> if (last == 'X') n += 30  else n += 50
            'C' -> if (last == 'X') n += 80  else n += 100
            'D' -> if (last == 'C') n += 300 else n += 500
            'M' -> if (last == 'C') n += 800 else n += 1000
        }
        last = c
    }
    return n
}

fun main(args: Array<String>) {
    val romans = arrayOf("I", "III", "IV", "VIII", "XLIX", "CCII", "CDXXXIII", "MCMXC", "MMVIII", "MDCLXVI")
    for (roman in romans) println("${roman.padEnd(10)} = ${romanDecode(roman)}")
}
Output:
I          = 1
III        = 3
IV         = 4
VIII       = 8
XLIX       = 49
CCII       = 202
CDXXXIII   = 433
MCMXC      = 1990
MMVIII     = 2008
MDCLXVI    = 1666

Lasso

define br => '\r'
//decode roman
define decodeRoman(roman::string)::integer => {
	local(ref = array('M'=1000, 'CM'=900, 'D'=500, 'CD'=400, 'C'=100, 'XC'=90, 'L'=50, 'XL'=40, 'X'=10, 'IX'=9, 'V'=5, 'IV'=4, 'I'=1))
	local(out = integer)
	while(#roman->size) => {
		// need to use neset while instead of query expr to utilize loop_abort
		while(loop_count <= #ref->size) => {
			if(#roman->beginswith(#ref->get(loop_count)->first)) => {
				#out += #ref->get(loop_count)->second
				#roman->remove(1,#ref->get(loop_count)->first->size)
				loop_abort
			}
		}
	}
	return #out
}

'MCMXC as integer is '+decodeRoman('MCMXC')
br
'MMVIII as integer is '+decodeRoman('MMVIII')
br
'MDCLXVI as integer is '+decodeRoman('MDCLXVI')

LiveScript

require! 'prelude-ls': {fold, sum}

# String → Number
decimal_of_roman = do
  # [Number, Number] → String → [Number, Number]
  _convert = ([acc, last_value], ch) -> 
    current_value = { M:1000 D:500 C:100 L:50 X:10 V:5 I:1 }[ch] ? 0
    op = if last_value < current_value then (-) else (+)
    [op(acc, last_value), current_value]
  # fold the string and sum the resulting tuple (array)
  fold(_convert, [0, 0]) >> sum

{[rom, decimal_of_roman rom] for rom in <[ MCMXC MMVII MDCLXVII MMMCLIX MCMLXXVII MMX ]>}

Output:

{"MCMXC":1990,"MMVII":2007,"MDCLXVII":1667,"MMMCLIX":3159,"MCMLXXVII":1977,"MMX":2010}

; Roman numeral decoder

; First, some useful substring utilities
to starts_with? :string :prefix
  if empty? :prefix [output "true]
  if empty? :string [output "false]
  if not equal? first :string first :prefix [output "false]
  output starts_with? butfirst :string butfirst :prefix
end

to remove_prefix :string :prefix 
  if or empty? :prefix not starts_with? :string :prefix [output :string]
  output remove_prefix butfirst :string butfirst :prefix
end

; Our list of Roman numeral values
make "values [[M 1000] [CM 900] [D  500] [CD 400] [C  100] [XC 90] [L 50]
              [XL 40]  [X  10]  [IX 9]   [V  5]   [IV 4]   [I  1]]

; Function to do the work
to from_roman :str
 local "n make "n 0
 foreach :values [
   local "s make "s first ?
   local "v make "v last ?
   while [starts_with? :str :s] [
     make "n sum n :v
     make "str remove_prefix :str :s
   ]
 ]
 output :n
end

foreach [MCMXC MDCLXVI MMVIII] [print (sentence (word ? "|: |) from_roman ?)]
bye
Output:
MCMXC:  1990
MDCLXVI:  1666
MMVIII:  2008

Lua

function ToNumeral( roman )
    local Num = { ["M"] = 1000, ["D"] = 500, ["C"] = 100, ["L"] = 50, ["X"] = 10, ["V"] = 5, ["I"] = 1 }
    local numeral = 0    
    
    local i = 1
    local strlen = string.len(roman)
    while i < strlen do
        local z1, z2 = Num[ string.sub(roman,i,i) ], Num[ string.sub(roman,i+1,i+1) ]
        if z1 < z2 then
            numeral = numeral + ( z2 - z1 )
            i = i + 2
        else
            numeral = numeral + z1
            i = i + 1    
        end        
    end
    
    if i <= strlen then numeral = numeral + Num[ string.sub(roman,i,i) ] end
    
    return numeral    
end

print( ToNumeral( "MCMXC" ) )
print( ToNumeral( "MMVIII" ) )
print( ToNumeral( "MDCLXVI" ) )
1990
2008
1666

M2000 Interpreter

Maximum Roman number is MMMCMXCIX (3999)

Module RomanNumbers {
      flush  ' empty current stack
      gosub Initialize
      document Doc$
      while not empty
            read rom$
            print rom$;"=";RomanEval$(rom$)
            Doc$=rom$+"="+RomanEval$(rom$)+{
            }
      end while
      Clipboard Doc$
      end
Initialize:
      function RomanEval$(rom$) {
            Flush
            ="invalid"
            if filter$(rom$,"MDCLXVI")<>"" Then Exit
            \\ "Y" is in top of stack
            Push "CM", "MD", "Q"
            Push "CD", "MD","W"
            Push "XC", "DL", "E"
            Push "XL", "X","R"
            Push "IX","V","T"
            Push  "IV","I","Y"
            \\ stack flush to doublerom
            doublerom=[]
            \\  "M" is in top of stack
            Data "M", 1000, "Q",900
            Data "D", 500,"W", 400
            Data "C",100,"E",90
            Data "L",50,"R", 40
            Data "X", 10, "T", 9
            Data "V", 5, "Y", 4, "I",1
            \\ stack flush to singlerom
            singlerom=[]
            acc=0
            value=0
            count=0
            stack doublerom {
                  if empty then exit
                  read rep$,exclude$,cc$
                  i=instr(rom$,cc$)
                  if i >0 then
                        tmp$=mid$(rom$,i+2)
                        L=Len(tmp$)
                        if L>0 then if Len(filter$(tmp$, exclude$))<>L then rom$="A": exit
                        if Instr(rom$,mid$(rom$,i,1))<i then rom$="A": exit
                        insert i,2 rom$=rep$  ' replace at pos i with rep$ and place a space to i+1
                  end if
                  loop
            }
            rom$=filter$(rom$," ") ' remove spaces if exist

            stack singlerom {
                  if empty then exit
                  read cc$, value
                  count=0
                  while left$(rom$,1)=cc$
                         insert 1, 1 rom$=""
                         count++
                         acc+=value            
                  end while
                  if count>3 then exit
                  loop
            }
            if len(rom$)>0  or count>3 Else
                  =Str$(acc,1033)
            end if      
      }
      data "MMMCMXCIX", "LXXIIX", "MMXVII", "LXXIX", "CXCIX","MCMXCIX","MMMDCCCLXXXVIII"
      data "CMXI","M","MCDXLIV","CCCC","IXV", "XLIXL","LXXIIX","IVM"
      data "XXXIX", "XXXX", "XIXX","IVI", "XLIX","XCIX","XCIV","XLVIII"
      return
}
RomanNumbers
Output:
MMMCMXCIX=3999
LXXIIX=invalid
MMXVII=2017
LXXIX=79
CXCIX=199
MCMXCIX=1999
MMMDCCCLXXXVIII=3888
CMXI=911
M=1000
MCDXLIV=1444
CCCC=invalid
IXV=invalid
XLIXL=invalid
LXXIIX=invalid
IVM=invalid
XXXIX=39
XXXX=invalid
XIXX=invalid
IVI=invalid
XLIX=49
XCIX=99
XCIV=94
XLVIII=48

Maple

f := n -> convert(n, arabic):
seq(printf("%a\n", f(i)), i in [MCMXC, MMVIII, MDCLXVI]);
Output:
1990
2008
1666

Mathematica/Wolfram Language

FromRomanNumeral["MMCDV"]
Output:
2405

MATLAB

function x = rom2dec(s)
% ROM2DEC converts Roman numbers to decimal

% store Roman digits values: I=1, V=5, X=10, L=50, C=100, D=500, M=1000
digitsValues = [0 0 100 500 0 0 0 0 1 0 0 50 1000 0 0 0 0 0 0 0 0 5 0 10 0 0];
% convert Roman number to array of values
values = digitsValues(s-'A'+1);
% change sign if next value is bigger
x = sum(values .* [sign(diff(-values)+eps),1]);

end

Here is a test:

romanNumbers = {'MMMCMXCIX', 'XLVIII', 'MMVIII'};
for n = 1 : numel(romanNumbers)
  fprintf('%10s = %4d\n',romanNumbers{n}, rom2dec(romanNumbers{n}));
end
Output:
 MMMCMXCIX = 3999
    XLVIII =   48
    MMVIII = 2008

Mercury

:- module test_roman.

:- interface.

:- import_module io.

:- pred main(io::di, io::uo) is det.

:- implementation.

:- import_module char.
:- import_module exception.
:- import_module int.
:- import_module list.
:- import_module string.

:- type conversion_error --->  not_a_roman_number.

:- func build_int(list(char), int, int) = int.
:- func from_roman(string) = int.
:- pred roman_to_int(char::in, int::out) is semidet.

from_roman(Roman) = Decimal :-
  List = reverse(to_char_list(Roman)),
  Decimal = build_int(List, 0, 0).

build_int([], LastValue, Accumulator) = LastValue + Accumulator.
build_int([Digit|Rest], LastValue, Accumulator) = Sum :-
  ( roman_to_int(Digit, Value) ->
      ( Value < LastValue ->
          Sum = build_int(Rest, Value, Accumulator - LastValue)
      ;   Sum = build_int(Rest, Value, Accumulator + LastValue) )
  ;   throw(not_a_roman_number) ).

roman_to_int('I', 1).
roman_to_int('V', 5).
roman_to_int('X', 10).
roman_to_int('L', 50).
roman_to_int('C', 100).
roman_to_int('D', 500).
roman_to_int('M', 1000).

main(!IO) :-
    command_line_arguments(Args, !IO),
    foldl((pred(Arg::in, !.IO::di, !:IO::uo) is det :-
              format("%s => %d\n", [s(Arg), i(from_roman(Arg))], !IO)),
          Args, !IO).

:- end_module test_roman.

Miranda

main :: [sys_message]
main = [ Stdout (s ++ ": " ++ show (fromroman s) ++ "\n")
       | s <- ["MCMXC", "MDCLXVI", "MMVII", "MMXXIII"]
       ]

fromroman :: [char]->num
fromroman = f
            where f []       = 0
                  f [x]      = r x
                  f (x:y:xs) = f (y:xs) - r x, if r x < r y
                             = f (y:xs) + r x, otherwise
                  r 'M' = 1000
                  r 'D' = 500
                  r 'C' = 100
                  r 'L' = 50
                  r 'X' = 10
                  r 'V' = 5
                  r 'I' = 1
Output:
MCMXC: 1990
MDCLXVI: 1666
MMVII: 2007
MMXXIII: 2023

Modula-2

MODULE RomanNumerals;
FROM InOut IMPORT WriteString, WriteCard, WriteLn;
FROM Strings IMPORT Length;

(* Convert given Roman numeral to binary *)
PROCEDURE DecodeRoman(s: ARRAY OF CHAR): CARDINAL;
    VAR i, d, len, acc: CARDINAL;
    
    PROCEDURE Digit(d: CHAR): CARDINAL;
    BEGIN
        CASE CHR( BITSET(ORD(d)) + BITSET{5} ) OF (* lowercase *)
            'm': RETURN 1000;
        |   'd': RETURN 500;
        |   'c': RETURN 100;
        |   'l': RETURN 50;
        |   'x': RETURN 10;
        |   'v': RETURN 5;
        |   'i': RETURN 1;
        ELSE
            RETURN 0;
        END;
    END Digit;
BEGIN
    len := Length(s);
    acc := 0;
    FOR i := 0 TO len-1 DO
        d := Digit(s[i]);
        IF d=0 THEN RETURN 0; END;
        
        IF (i # len-1) AND (d < Digit(s[i+1])) THEN
            acc := acc - d;
        ELSE
            acc := acc + d;
        END;
    END;
    RETURN acc;
END DecodeRoman;

PROCEDURE Show(s: ARRAY OF CHAR);
BEGIN
    WriteString(s);
    WriteString(": ");
    WriteCard(DecodeRoman(s), 0);
    WriteLn();
END Show;

BEGIN
    Show("MCMXC");
    Show("MDCLXVI");
    Show("mmvii");
    Show("mmxxi");
END RomanNumerals.
Output:
MCMXC: 1990
MDCLXVI: 1666
mmvii: 2007
mmxxi: 2021

Nanoquery

Translation of: Java
def decodeSingle(letter)
	if letter = "M"
		return 1000
	else if letter = "D"
		return 500
	else if letter =  "C"
		return 100
	else if letter = "L"
		return 50
	else if letter = "X"
		return 10
	else if letter = "V"
		return 5
	else if letter = "I"
		return 1
	else
		return 0
	end
end

def decode(roman)
	result = 0
	uRoman = roman.toUpperCase()
	
	for (i = 0) (i < len(uRoman) - 1) (i += 1)
		if decodeSingle(uRoman[i]) < decodeSingle(uRoman[i + 1])
			result -= decodeSingle(uRoman[i])
		else
			result += decodeSingle(uRoman[i])
		end
	end
	
	result += decodeSingle(uRoman[len(uRoman) - 1])
	return result
end

println decode("MCMXC")
println decode("MMVIII")
println decode("MDCLXVI")
Output:
1990
2008
1666

NetRexx

/* NetRexx */
options replace format comments java crossref savelog symbols binary

           /* 1990  2008   1666    */
years = Rexx('MCMXC MMVIII MDCLXVI')

loop y_ = 1 to years.words
    Say years.word(y_).right(10) || ':' decode(years.word(y_))
    end y_

return

method decode(arg) public static returns int signals IllegalArgumentException

  parse arg.upper roman .
  if roman.verify('MDCLXVI') \= 0 then signal IllegalArgumentException

  -- always insert the value of the least significant numeral
  decnum = rchar(roman.substr(roman.length, 1))
  loop d_ = 1 to roman.length - 1
    if rchar(roman.substr(d_, 1)) < rchar(roman.substr(d_ + 1, 1)) then do
      -- Handle cases where numerals are not in descending order
      --   subtract the value of the numeral
      decnum = decnum - rchar(roman.substr(d_, 1))
      end
    else do
      -- Normal case
      --   add the value of the numeral
      decnum = decnum + rchar(roman.substr(d_, 1))
      end
    end d_

  return decnum

method rchar(arg) public static returns int

  parse arg.upper ch +1 .
  select case ch
    when 'M' then digit = 1000
    when 'D' then digit =  500
    when 'C' then digit =  100
    when 'L' then digit =   50
    when 'X' then digit =   10
    when 'V' then digit =    5
    when 'I' then digit =    1
    otherwise     digit =    0
    end

  return digit
Output:
     MCMXC: 1990
    MMVIII: 2008
   MDCLXVI: 1666

Nim

Translation of: Python
import tables

let rdecode = {'M': 1000, 'D': 500, 'C': 100, 'L': 50, 'X': 10, 'V': 5, 'I': 1}.toTable

proc decode(roman: string): int =
  for i in 0 ..< roman.high:
    let (rd, rd1) = (rdecode[roman[i]], rdecode[roman[i+1]])
    result += (if rd < rd1: -rd else: rd)
  result += rdecode[roman[roman.high]]

for r in ["MCMXC", "MMVIII", "MDCLXVI"]:
  echo r, " ", decode(r)
Output:
MCMXC 1990
MMVIII 2008
MDCLXVI 1666

OCaml

let decimal_of_roman roman =
  let arabic = ref 0 in
  let lastval = ref 0 in
  for i = (String.length roman) - 1 downto 0 do
    let n =
      match roman.[i] with
      | 'M' | 'm' -> 1000
      | 'D' | 'd' -> 500
      | 'C' | 'c' -> 100
      | 'L' | 'l' -> 50
      | 'X' | 'x' -> 10
      | 'V' | 'v' -> 5
      | 'I' | 'i' -> 1
      | _ -> 0
    in
    if n < !lastval
    then arabic := !arabic - n
    else arabic := !arabic + n;
    lastval := n
  done;
  !arabic

let () =
  Printf.printf " %d\n" (decimal_of_roman "MCMXC");
  Printf.printf " %d\n" (decimal_of_roman "MMVIII");
  Printf.printf " %d\n" (decimal_of_roman "MDCLXVI");
;;

Another implementation

Another implementation, a bit more OCaml-esque: no mutable variables, and a recursive function instead of a for loop.

Works with: OCaml version 4.03+
(* Scan the roman number from right to left. *)
(* When processing a roman digit, if the previously processed roman digit was
 * greater than the current one, we must substract the latter from the current
 * total, otherwise add it.
 * Example:
 * - MCMLXX read from right to left is XXLMCM
 *          the sum is 10 + 10 + 50 + 1000 - 100 + 1000 *)
let decimal_of_roman roman =
  (* Use 'String.uppercase' for OCaml 4.02 and previous. *)
  let rom = String.uppercase_ascii roman in
  (* A simple association list. IMHO a Hashtbl is a bit overkill here. *)
  let romans = List.combine ['I'; 'V'; 'X'; 'L'; 'C'; 'D'; 'M']
                            [1; 5; 10; 50; 100; 500; 1000] in
  let compare x y =
    if x < y then -1 else 1
  in
  (* Scan the string from right to left using index i, and keeping track of
   * the previously processed roman digit in prevdig. *)
  let rec doloop i prevdig =
    if i < 0 then 0 
    else
      try
        let currdig = List.assoc rom.[i] romans in
        (currdig * compare currdig prevdig) + doloop (i - 1) currdig
      with
        (* Ignore any incorrect roman digit and just process the next one. *)
        Not_found -> doloop (i - 1) 0
  in
  doloop (String.length rom - 1) 0


(* Some simple tests. *)
let () =
  let testit roman decimal =
    let conv = decimal_of_roman roman in
    let status = if conv = decimal then "PASS" else "FAIL" in
    Printf.sprintf "[%s] %s\tgives %d.\tExpected: %d.\t"
                   status roman conv decimal
  in
  print_endline ">>> Usual roman numbers.";
  print_endline (testit "MCMXC" 1990);
  print_endline (testit "MMVIII" 2008);
  print_endline (testit "MDCLXVI" 1666);
  print_newline ();

  print_endline ">>> Roman numbers with lower case letters are OK.";
  print_endline (testit "McmXC" 1990);
  print_endline (testit "MMviii" 2008);
  print_endline (testit "mdCLXVI" 1666);
  print_newline ();

  print_endline ">>> Incorrect roman digits are ignored.";
  print_endline (testit "McmFFXC" 1990);
  print_endline (testit "MMviiiPPPPP" 2008);
  print_endline (testit "mdCLXVI_WHAT_NOW" 1666);
  print_endline (testit "2 * PI ^ 2" 1);  (* The I in PI... *)
  print_endline (testit "E = MC^2" 1100)

Output:

>>> Usual roman numbers.
[PASS] MCMXC	gives 1990.	Expected: 1990.	
[PASS] MMVIII	gives 2008.	Expected: 2008.	
[PASS] MDCLXVI	gives 1666.	Expected: 1666.	

>>> Roman numbers with lower case letters are OK.
[PASS] McmXC	gives 1990.	Expected: 1990.	
[PASS] MMviii	gives 2008.	Expected: 2008.	
[PASS] mdCLXVI	gives 1666.	Expected: 1666.	

>>> Incorrect roman digits are ignored.
[PASS] McmFFXC	gives 1990.	Expected: 1990.	
[PASS] MMviiiPPPPP	gives 2008.	Expected: 2008.	
[PASS] mdCLXVI_WHAT_NOW	gives 1666.	Expected: 1666.	
[PASS] 2 * PI ^ 2	gives 1.	Expected: 1.	
[PASS] E = MC^2	gives 1100.	Expected: 1100.	

PARI/GP

fromRoman(s)={
  my(v=Vecsmall(s),key=vector(88),cur,t=0,tmp);
  key[73]=1;key[86]=5;key[88]=10;key[76]=50;key[67]=100;key[68]=500;key[77]=1000;
  cur=key[v[1]];
  for(i=2,#v,
    tmp=key[v[i]];
    if(!cur, cur=tmp; next);
    if(tmp>cur,
      t+=tmp-cur;
      cur=0
    ,
      t+=cur;
      cur=tmp
    )
  );
  t+cur
};

Perl

use 5.10.0;

{
 my @trans = (
                [M  => 1000],     [CM => 900], 
                [D  => 500],      [CD => 400], 
                [C  => 100],      [XC => 90],
                [L  => 50],       [XL => 40],
                [X  => 10],       [IX => 9],
                [V  => 5],        [IV => 4],
                [I  => 1],
 );

  sub from_roman {
        my $r = shift;
        my $n = 0;
        foreach my $pair (@trans) {
          my ($k, $v) = @$pair;
          $n += $v while $r =~ s/^$k//i;
        }
        return $n
  }
}

say "$_: ", from_roman($_) for qw(MCMXC MDCLXVI MMVIII);
Output:
MCMXC: 1990
MDCLXVI: 1666
MMVIII: 2008

Alternate

#!/usr/bin/perl

use strict;
use warnings;

sub roman2decimal
  {
  (local $_, my $sum, my $zeros) = (shift, 0, '');
  $zeros .= 0 while
    $sum -= s/I(?=[VX])// - s/V// * 5 - s/I//g . $zeros,
    tr/MDCLX/CLXVI/;
  return $sum;
  }

print s/$/ ": " . roman2decimal($_) /er while <DATA>;

__DATA__
MCMXC
MMVIII
MDCLXVI
Output:
MCMXC: 1990
MMVIII: 2008
MDCLXVI: 1666

Another Alternate

#!/usr/bin/perl

use strict;
use warnings;

sub roman2decimal
  {
  my $sum = 0;
  $sum += $^R while $_[0] =~
    / M  (?{1000})
    | D  (?{ 500})
    | C  (?{ 100}) (?= [MD] (?{-100}) )?
    | L  (?{  50})
    | X  (?{  10}) (?= [CL] (?{ -10}) )?
    | V  (?{   5})
    | I  (?{   1}) (?= [XV] (?{  -1}) )?
    /gx;
  return $sum;
  }

print s/$/ ": " . roman2decimal($_) /er while <DATA>;

__DATA__
MCMXC
MMVIII
MDCLXVI
Output:
MCMXC: 1990
MMVIII: 2008
MDCLXVI: 1666

Phix

with javascript_semantics
function romanDec(string s)
    integer res = 0, prev = 0
    for i=length(s) to 1 by -1 do
        integer rdx = find(upper(s[i]),"IVXLCDM"),
                rn = power(10,floor((rdx-1)/2))
        if even(rdx) then rn *= 5 end if
        res += iff(rn<prev?-rn:rn)
        prev = rn
    end for
    return {s,res} -- (for output)
end function

?apply({"MCMXC","MMVIII","MDCLXVI"},romanDec)
Output:
{{"MCMXC",1990},{"MMVIII",2008},{"MDCLXVI",1666}}

cheating slightly

with javascript_semantics
requires("1.0.5")
function romanDec(string s)
    return {s,scanf(s,"%R")[1][1]}
end function

same output, if applied the same way as above, error handling omitted

Phixmonti

def romanDec   /# s -- n #/
    0 >ps 0 >ps
    ( ( "M" 1000 ) ( "D" 500 ) ( "C" 100 ) ( "L" 50 ) ( "X" 10 ) ( "V" 5 ) ( "I" 1 ) )
    
    swap upper reverse
    len while
        pop rot rot tochar getd
        if
            dup ps> < if 0 swap - endif
            dup ps> + >ps
            >ps
            swap
        else
            "Error: " print ? ""
        endif
        len
    endwhile
    drop drop
    ps> drop ps>
enddef

/# usage example: "MMXX" romanDec ? (show 2020) #/

More traditional solution:

"MDCLXVI" var romans
( 1000 500 100 50 10 5 1 ) var decmls

def romanDec    /# s -- n #/
    0 var prev
    0 var res
    upper
    ( len 1 -1 ) for
        get
        romans swap find nip
        dup if
            decmls swap get nip
            dup prev < if 0 swap - endif
            dup res + var res
            var prev
        else
            "Error" ? 0 var res exitfor
        endif
    endfor
    drop
    res
enddef

PHP

<?php
/**
 * @author Elad Yosifon
 */
$roman_to_decimal = array(
	'I' => 1,
	'V' => 5,
	'X' => 10,
	'L' => 50,
	'C' => 100,
	'D' => 500,
	'M' => 1000,
);

/**
 * @param $number
 * @return int
 */
function roman2decimal($number)
{
	global $roman_to_decimal;

	// breaks the string into an array of chars
	$digits = str_split($number);
	$lastIndex = count($digits)-1;
	$sum = 0;

	foreach($digits as $index => $digit)
	{
		if(!isset($digits[$index]))
		{
			continue;
		}

		if(isset($roman_to_decimal[$digit]))
		{
			if($index < $lastIndex)
			{
				$left = $roman_to_decimal[$digits[$index]];
				$right = $roman_to_decimal[$digits[$index+1]];
				if($left < $right)
				{
					$sum += ($right - $left);
					unset($digits[$index+1],$left, $right);
					continue;
				}
				unset($left, $right);
			}
		}
		$sum += $roman_to_decimal[$digit];
	}

	return $sum;
}

/*============= OUTPUT =============*/
header('Content-Type: text/plain');

$tests = array(
	"I" => array(roman2decimal('I'), 1),
	"II" => array(roman2decimal('II'), 2),
	"III" => array(roman2decimal('III'), 3),
	"IV" => array(roman2decimal('IV'), 4),
	"V" => array(roman2decimal('V'), 5),
	"VI" => array(roman2decimal('VI'), 6),
	"VII" => array(roman2decimal('VII'), 7),
	"IX" => array(roman2decimal('IX'), 9),
	"X" => array(roman2decimal('X'), 10),
	"XI" => array(roman2decimal('XI'), 11),
	"XIV" => array(roman2decimal('XIV'), 14),
	"XV" => array(roman2decimal('XV'), 15),
	"XVI" => array(roman2decimal('XVI'), 16),
	"XVIV" => array(roman2decimal('XVIV'), 19),
	"XIX" => array(roman2decimal('XIX'), 19),
	"MDCLXVI" => array(roman2decimal('MDCLXVI'), 1666),
	"MCMXC" => array(roman2decimal('MCMXC'), 1990),
	"MMVIII" => array(roman2decimal('MMVIII'), 2008),
	"MMMCLIX" => array(roman2decimal('MMMCLIX'), 3159),
	"MCMLXXVII" => array(roman2decimal('MCMLXXVII'), 1977),
);


foreach($tests as $key => $value)
{
	echo "($key == {$value[0]}) => " . ($value[0] === $value[1] ? "true" : "false, should be {$value[1]}.") . "\n";
}
Output:
(I == 1) => true
(II == 2) => true
(III == 3) => true
(IV == 4) => true
(V == 5) => true
(VI == 6) => true
(VII == 7) => true
(IX == 9) => true
(X == 10) => true
(XI == 11) => true
(XIV == 14) => true
(XV == 15) => true
(XVI == 16) => true
(XVIV == 19) => true
(XIX == 19) => true
(MDCLXVI == 1666) => true
(MCMXC == 1990) => true
(MMVIII == 2008) => true
(MMMCLIX == 3159) => true
(MCMLXXVII == 1977) => true

Picat

go => 
   List = ["IV",
           "XLII",
           "M",
           "MCXI",
           "CMXI",
           "MCM",
           "MCMXC",
           "MMVIII",
           "MMIX",
           "MCDXLIV",
           "MDCLXVI",
           "MMXII"],
   foreach(R in List) 
      printf("%-8s: %w\n", R, roman_decode(R))
   end,
   nl.


roman_decode(Str) = Res =>
  if Str == "" then
    Res := ""
  else 
    D = new_map(findall((R=D), roman(R,D))),
    Res = 0,
    Old = 0,
    foreach(S in Str)
      N = D.get(S),
      % Fix for the Roman convention that XC = 90, not 110.
      if Old > 0, N > Old then
        Res := Res - 2*Old
      end,
      Res := Res + N,
      Old := N 
    end
  end.

roman('I', 1).
roman('V', 5).
roman('X', 10).
roman('L', 50).
roman('C', 100).
roman('D', 500).
roman('M', 1000).
Output:
IV      : 4
XLII    : 42
M       : 1000
MCXI    : 1111
CMXI    : 911
MCM     : 1900
MCMXC   : 1990
MMVIII  : 2008
MMIX    : 2009
MCDXLIV : 1444
MDCLXVI : 1666
MMXII   : 2012


PicoLisp

(de roman2decimal (Rom)
   (let L (replace (chop Rom) 'M 1000 'D 500 'C 100 'L 50 'X 10 'V 5 'I 1)
      (sum '((A B) (if (>= A B) A (- A))) L (cdr L)) ) )

Test:

: (roman2decimal "MCMXC")
-> 1990

: (roman2decimal "MMVIII")
-> 2008

: (roman2decimal "MDCLXVI")
-> 1666

PL/I

test_decode: procedure options (main); /* 28 January 2013 */
   declare roman character (20) varying;

   do roman = 'i', 'ii', 'iii', 'iv', 'v', 'vi', 'vii', 'viii', 'iix',
              'ix', 'x', 'xi', 'xiv', 'MCMLXIV', 'MCMXC', 'MDCLXVI',
              'MIM', 'MM', 'MMXIII';
      put skip list (roman, decode(roman));
   end;

decode: procedure (roman) returns (fixed(15));
   declare roman character (*) varying;
   declare (current, previous) character (1);
   declare n fixed (15);
   declare i fixed binary;

   previous = ''; n = 0;
   do i = length(roman) to 1 by -1;
      current = substr(roman, i, 1);
      if digit_value(current) < digit_value(previous) then
         n = n - digit_value(current);
      else if digit_value(current) > digit_value(previous) then
         do;
            n = n + digit_value(current);
            previous = current;
         end;
      else
         n = n + digit_value(current);
   end;
   return (n);
end decode;

digit_value: procedure (roman_char) returns (fixed);
   declare roman_char character(1);
      select (roman_char);
         when ('M', 'm') return (1000);
         when ('D', 'd') return (500);
         when ('C', 'c') return (100);
         when ('L', 'l') return (50);
         when ('X', 'x') return (10);
         when ('V', 'v') return (5);
         when ('I', 'i') return (1);
         otherwise       return (0);
      end;
end digit_value;

end test_decode;
i                                        1 
ii                                       2 
iii                                      3 
iv                                       4 
v                                        5 
vi                                       6 
vii                                      7 
viii                                     8 
iix                                      8 
ix                                       9 
x                                       10 
xi                                      11 
xiv                                     14 
MCMLXIV                               1964 
MCMXC                                 1990 
MDCLXVI                               1666 
MIM                                   1999 
MM                                    2000 
MMXIII                                2013 

PL/M

100H:
/* CP/M CALLS */
BDOS: PROCEDURE (FN, ARG); DECLARE FN BYTE, ARG ADDRESS; GO TO 5; END BDOS;
EXIT: PROCEDURE; CALL BDOS(0,0); END EXIT;
PRINT: PROCEDURE (S); DECLARE S ADDRESS; CALL BDOS(9,S); END PRINT;

/* CP/M COMMAND LINE ARGUMENT */
DECLARE ARG$LPTR ADDRESS INITIAL (80H), ARG$LEN BASED ARG$LPTR BYTE;
DECLARE ARG$PTR  ADDRESS INITIAL (81H), ARG     BASED ARG$PTR BYTE;

/* CONVERT ROMAN NUMERAL TO BINARY */
READ$ROMAN: PROCEDURE (RP) ADDRESS;
    DECLARE DIGITS (7) BYTE INITIAL ('MDCLXVI');
    DECLARE VALUES (7) ADDRESS INITIAL (1000,500,100,50,10,5,1);
    DECLARE (RP, V, DVAL) ADDRESS, R BASED RP BYTE;
    V = 0;
    
    GET$DIGIT: PROCEDURE (D) ADDRESS;
        DECLARE (D, I) BYTE;
        DO I = 0 TO LAST(DIGITS);
            IF DIGITS(I) = D THEN RETURN VALUES(I);
        END;
        RETURN 0; /* NOT FOUND */
    END GET$DIGIT;
        
    DO WHILE R <> '$';
        DVAL = GET$DIGIT(R);
        IF DVAL = 0 THEN RETURN 0; /* ERROR */
        RP = RP + 1;
        IF GET$DIGIT(R) > DVAL THEN
            V = V - DVAL; /* SUBTRACTIVE PRINCIPLE */
        ELSE
            V = V + DVAL;
    END;
    RETURN V;
END READ$ROMAN;

/* PRINT BINARY NUMBER AS DECIMAL */
PRINT$NUMBER: PROCEDURE (N);
    DECLARE S (6) BYTE INITIAL ('.....$');
    DECLARE (N, P) ADDRESS, C BASED P BYTE;
    P = .S(5);
DIGIT:
    P = P - 1;
    C = N MOD 10 + '0';
    N = N / 10;
    IF N > 0 THEN GO TO DIGIT;
    CALL PRINT(P);
END PRINT$NUMBER;

IF ARG$LEN = 0 THEN DO;
    CALL PRINT(.'NO INPUT$');
    CALL EXIT;
END;

ARG(ARG$LEN) = '$';  /* TERMINATE ARGUMENT STRING */
CALL PRINT(.ARG(1)); /* PRINT ROMAN NUMERAL */
CALL PRINT(.': $');
CALL PRINT$NUMBER(READ$ROMAN(.ARG(1))); /* CONVERT AND PRINT VALUE */
CALL EXIT;
EOF
Output:
A>ROMAN MCMXC
MCMXC: 1990
A>ROMAN MDCLXVI
MDCLXVI: 1666
A>ROMAN MMVII
MMVII: 2007
A>ROMAN MMXXI
MMXXI: 2021

PL/SQL

/*****************************************************************
 * $Author: Atanas Kebedjiev $
 *****************************************************************
 * PL/SQL code can be run as anonymous block.
 * To test, execute the whole script or create the functions and then e.g. 'select rdecode('2012') from dual;
 * Please note that task definition does not describe fully some current rules, such as
 * * subtraction - IX XC CM are the valid subtraction combinations
 * * A subtraction character cannot be repeated: 8 is expressed as VIII and not as IIX
 * * V L and D cannot be used for subtraction
 * * Any numeral cannot be repeated more than 3 times: 1910 should be MCMX and not MDCCCCX
 * Code below does not validate the Roman numeral itself and will return a result even for a non-compliant number
 * E.g. both MCMXCIX and IMM will return 1999 but the first one is the correct notation
 */

DECLARE

FUNCTION rvalue(c IN CHAR) RETURN NUMBER IS
    i INTEGER;
BEGIN
    i := 0;
    CASE (c)
        when 'M' THEN i := 1000;
        when 'D' THEN i := 500;
        when 'C' THEN i := 100;
        when 'L' THEN i := 50;
        when 'X' THEN i := 10;
        when 'V' THEN i := 5;
        when 'I' THEN i := 1;
    END CASE;
    RETURN i;
END;


FUNCTION decode(rn IN VARCHAR2) RETURN NUMBER IS
   i  INTEGER;
   l  INTEGER;
   cr CHAR;   -- current Roman numeral as substring from r
   cv INTEGER; -- value of current Roman numeral

   gr CHAR;   -- next Roman numeral
   gv NUMBER; --  value of the next numeral;

   dv NUMBER; -- decimal value to return
BEGIN
           l := length(rn);
           i := 1;
           dv := 0;
           while (i <= l)
           LOOP
                cr := substr(rn,i,1);
                cv := rvalue(cr);

   /* Look for a larger numeral in next position, like IV or CM  
      The number to subtract should be at least 1/10th of the bigger number
      CM and XC are valid, but IC and XM are not */
                IF (i < l) THEN
                   gr := substr(rn,i+1,1);
                   gv := rvalue(gr);
                   IF (cv < gv ) THEN
                      dv := dv - cv;
                   ELSE
                      dv := dv + cv;
                   END IF;
                ELSE
                   dv := dv + cv;
                END IF;  -- need to add the last value unconditionally

                i := i + 1;
            END LOOP;

RETURN dv;

END;

BEGIN

    DBMS_OUTPUT.PUT_LINE ('MMXII      = ' || rdecode('MMXII'));       -- 2012
    DBMS_OUTPUT.PUT_LINE ('MCMLI      = ' || rdecode('MCMLI'));       -- 1951
    DBMS_OUTPUT.PUT_LINE ('MCMLXXXVII = ' || rdecode('MCMLXXXVII'));  -- 1987
    DBMS_OUTPUT.PUT_LINE ('MDCLXVI    = ' || rdecode('MDCLXVI'));     -- 1666
    DBMS_OUTPUT.PUT_LINE ('MCMXCIX    = ' || rdecode('MCMXCIX'));     -- 1999

END;

PowerShell

Filter FromRoman {
	$output = 0
	
	if ($_ -notmatch '^(M{1,3}|)(CM|CD|D?C{0,3}|)(XC|XL|L?X{0,3}|)(IX|IV|V?I{0,3}|)$') {
		throw 'Incorrect format'
	}
	
	$current = 1000
	$subtractor = 'M'
	$whole = $False
	$roman = $_
	'C','D','X','L','I','V',' ' `
	| %{
		if ($whole = !$whole) {
			$current /= 10
			$subtractor = $_ + $subtractor[0]
			$_ = $subtractor[1]
		}
		else {
			$subtractor = $subtractor[0] + $_
		}
		
		if ($roman -match $subtractor) {
			$output += $current * (4,9)[$whole]
			$roman = $roman -replace $subtractor,''
		}
		if ($roman -match ($_ + '{1,3}')) {
			$output += $current * (5,10)[$whole] * $Matches[0].Length
		}
	}
	
	$output
}
'XIX','IV','','MMCDLXXIX','MMMI' | FromRoman
Output:
19
4
0
2479
3001

Prolog

decode_digit(i, 1).
decode_digit(v, 5).
decode_digit(x, 10).
decode_digit(l, 50).
decode_digit(c, 100).
decode_digit(d, 500).
decode_digit(m, 1000).

decode_string(Sum, _, [], Sum).

decode_string(LastSum, LastValue, [Digit|Rest], NextSum) :-
   decode_digit(Digit, Value),
   Value < LastValue,
   Sum is LastSum - Value,
   decode_string(Sum, Value, Rest, NextSum).

decode_string(LastSum, LastValue, [Digit|Rest], NextSum) :-
   decode_digit(Digit, Value),
   Value >= LastValue,
   Sum is LastSum + Value,
   decode_string(Sum, Value, Rest, NextSum).

decode_string(Atom, Value) :-
   atom_chars(Atom, String),
   reverse(String, [Last|Rest]),
   decode_digit(Last, Start),
   decode_string(Start, Start, Rest, Value).

test :-
   decode_string(mcmxc, 1990),
   decode_string(mmviii, 2008),
   decode_string(mdclxvi, 1666).

The program above contains its own test predicate. The respective goal succeeds. Therefore the test passes.

Python

Imperative

_rdecode = dict(zip('MDCLXVI', (1000, 500, 100, 50, 10, 5, 1)))

def decode( roman ):
    result = 0
    for r, r1 in zip(roman, roman[1:]):
        rd, rd1 = _rdecode[r], _rdecode[r1]
        result += -rd if rd < rd1 else rd
    return result + _rdecode[roman[-1]]

if __name__ == '__main__':
    for r in 'MCMXC MMVIII MDCLXVI'.split():
        print( r, decode(r) )
Output:
MCMXC 1990
MMVIII 2008
MDCLXVI 1666

Another version, which I believe has clearer logic:

roman_values = (('I',1), ('IV',4), ('V',5), ('IX',9),('X',10),('XL',40),('L',50),('XC',90),('C',100),
                    ('CD', 400), ('D', 500), ('CM', 900), ('M',1000))

def roman_value(roman):
    total=0
    for symbol,value in reversed(roman_values):
        while roman.startswith(symbol):
            total += value
            roman = roman[len(symbol):]
    return total

if __name__=='__main__':
    for value in "MCMXC", "MMVIII", "MDCLXVI":
        print('%s = %i' % (value, roman_value(value)))
Output:
MCMXC = 1990
MMVIII = 2008
MDCLXVI = 1666


Declarative

Less clear, but a 'one liner':

numerals = { 'M' : 1000, 'D' : 500, 'C' : 100, 'L' : 50, 'X' : 10, 'V' : 5, 'I' : 1 }
def romannumeral2number(s):
        return reduce(lambda x, y: -x + y if x < y else x + y, map(lambda x: numerals.get(x, 0), s.upper()))


Or, defining intFromRoman as a fold or reduction, and annotating a little more fully:

Translation of: Haskell
Works with: Python version 3
'''Roman numerals decoded'''

from operator import mul
from functools import reduce
from collections import defaultdict
from itertools import accumulate, chain, cycle


# intFromRoman :: String -> Maybe Int
def intFromRoman(s):
    '''Just the integer represented by a Roman
       numeral string, or Nothing if any
       characters are unrecognised.
    '''
    dct = defaultdict(
        lambda: None,
        zip(
            'IVXLCDM',
            accumulate(chain([1], cycle([5, 2])), mul)
        )
    )

    def go(mb, x):
        '''Just a letter value added to or
           subtracted from a total, or Nothing
           if no letter value is defined.
        '''
        if None in (mb, x):
            return None
        else:
            r, total = mb
            return x, total + (-x if x < r else x)

    return bindMay(reduce(
        go,
        [dct[k.upper()] for k in reversed(list(s))],
        (0, 0)
    ))(snd)


# ------------------------- TEST -------------------------
def main():
    '''Testing a sample of dates.'''

    print(
        fTable(__doc__ + ':\n')(str)(
            maybe('(Contains unknown character)')(str)
        )(
            intFromRoman
        )([
            "MDCLXVI", "MCMXC", "MMVIII",
            "MMXVI", "MMXVIII", "MMZZIII"
        ])
    )


# ----------------------- GENERIC ------------------------

# bindMay (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
def bindMay(m):
    '''Injection operator for the Maybe monad.
       If m is Nothing, it is passed straight through.
       If m is Just(x), the result is an application
       of the (a -> Maybe b) function (mf) to x.'''
    return lambda mf: (
        m if None is m else mf(m)
    )


# maybe :: b -> (a -> b) -> Maybe a -> b
def maybe(v):
    '''Either the default value v, if m is Nothing,
       or the application of f to x,
       where m is Just(x).
    '''
    return lambda f: lambda m: v if None is m else (
        f(m)
    )


# snd :: (a, b) -> b
def snd(ab):
    '''Second member of a pair.'''
    return ab[1]


# ---------------------- FORMATTING ----------------------

# fTable :: String -> (a -> String) ->
# (b -> String) -> (a -> b) -> [a] -> String
def fTable(s):
    '''Heading -> x display function ->
       fx display function -> f -> xs -> tabular string.
    '''
    def go(xShow, fxShow, f, xs):
        ys = [xShow(x) for x in xs]
        w = max(map(len, ys))
        return s + '\n' + '\n'.join(map(
            lambda x, y: (
                f'{y.rjust(w, " ")} -> {fxShow(f(x))}'
            ),
            xs, ys
        ))
    return lambda xShow: lambda fxShow: lambda f: (
        lambda xs: go(xShow, fxShow, f, xs)
    )


# MAIN ---
if __name__ == '__main__':
    main()
Output:
Roman numerals decoded:

MDCLXVI -> 1666
  MCMXC -> 1990
 MMVIII -> 2008
  MMXVI -> 2016
MMXVIII -> 2018
MMZZIII -> (Contains unknown character)

Quackery

  [ 2dup <
    if
      [ dip
          [ 2 * - ]
        dup ] 
    nip dup
    rot + swap ] is roman    ( t p n --> t p )
 
  [    1 roman ] is I        (   t p --> t p )
  [    5 roman ] is V        (   t p --> t p )
  [   10 roman ] is X        (   t p --> t p )
  [   50 roman ] is L        (   t p --> t p )
  [  100 roman ] is C        (   t p --> t p )
  [  500 roman ] is D        (   t p --> t p )
  [ 1000 roman ] is M        (   t p --> t p )
 
  [ 0 1000 rot
    $ "" swap
    witheach
      [ space join
        join ]
    quackery     
    drop ]       is ->arabic (     $ --> n   )
 
  $ "  MCMXC" dup echo$ say " = " ->arabic echo cr
  $ " MMVIII" dup echo$ say " = " ->arabic echo cr
  $ "MDCLXVI" dup echo$ say " = " ->arabic echo cr
  cr 
  $ "I MIX VIVID MILD MIMIC"
               dup echo$ say " = " ->arabic echo cr
Output:
  MCMXC = 1990
 MMVIII = 2008
MDCLXVI = 1666

I MIX VIVID MILD MIMIC = 3063


R

version 1

Modelled along the lines of other decode routines on this page, but using a vectorised approach

romanToArabic <- function(roman) {
  romanLookup <- c(I=1L, V=5L, X=10L, L=50L, C=100L, D=500L, M=1000L)
  rSplit <- strsplit(toupper(roman), character(0)) # Split input vector into characters
  toArabic <- function(item) {
    digits <- romanLookup[item]
    if (length(digits) > 1L) {
      smaller <- (digits[-length(digits)] < digits[-1L])
      digits[smaller] <- - digits[smaller]
    }
    sum(digits)
  }
  vapply(rSplit, toArabic, integer(1))
}

Example usage:

romanToArabic(c("MCMXII", "LXXXVI"))

version 2

Using built-in functionality in R

as.integer(as.roman(c("MCMXII", "LXXXVI"))

Racket

#lang racket
(define (decode/roman number)
  (define letter-values
    (map cons '(#\M #\D #\C #\L #\X #\V #\I) '(1000 500 100 50 10 5 1)))
  (define (get-value letter)
    (cdr (assq letter letter-values)))
  (define lst (map get-value (string->list number)))
  (+ (last lst)
     (for/fold ((sum 0))
       ((i (in-list lst)) (i+1 (in-list (cdr lst))))
       (+ sum
          (if (> i+1 i)
              (- i)
              i)))))

(map decode/roman '("MCMXC" "MMVIII" "MDCLXVI"))
;-> '(1990 2008 1666)

Raku

(formerly Perl 6) A non-validating version:

sub rom-to-num($r) {
    [+] gather $r.uc ~~ /
        ^
        [
        | M  { take 1000 }
        | CM { take 900 }
        | D  { take 500 }
        | CD { take 400 }
        | C  { take 100 }
        | XC { take 90 }
        | L  { take 50 }
        | XL { take 40 }
        | X  { take 10 }
        | IX { take 9 }
        | V  { take 5 }
        | IV { take 4 }
        | I  { take 1 }
        ]+
        $
    /;
}

say "$_ => &rom-to-num($_)" for <MCMXC MDCLXVI MMVIII>;
Output:
MCMXC => 1990
MDCLXVI => 1666
MMVIII => 2008

A validating version. Also handles older forms such as 'IIXX' and "IIII".

sub rom-to-num($r) {
    [+] gather $r.uc ~~ /
        ^
        ( (C*)M { take 1000 - 100 * $0.chars } )*
        ( (C*)D { take  500 - 100 * $0.chars } )?
        ( (X*)C { take  100 -  10 * $0.chars } )*
        ( (X*)L { take   50 -  10 * $0.chars } )?
        ( (I*)X { take   10 -       $0.chars } )*
        ( (I*)V { take    5 -       $0.chars } )?
        (     I { take    1                  } )*
        [ $ || { return NaN } ]
    /;
}

say "$_ => ", rom-to-num($_) for <MCMXC mdclxvi MMViii IIXX ILL>;
Output:
MCMXC => 1990
mdclxvi => 1666
MMViii => 2008
IIXX => 18
ILL => NaN

Red

version 1

Red [
    Purpose: "Arabic <-> Roman numbers converter"
    Author: "Didier Cadieu"
    Date: "07-Oct-2016"
]

table-r2a: reverse [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"]

roman-to-arabic: func [r [string!] /local a b e] [
	a: 0
	parse r [any [b: ["I" ["V" | "X" | none] | "X" ["L" | "C" | none] | "C" ["D" | "M" | none] | "V" | "L" | "D" | "M"] e: (a: a + select table-r2a copy/part b e)]]
	a
]

; Example usage:
print roman-to-arabic "XXXIII"
print roman-to-arabic "MDCCCLXXXVIII"
print roman-to-arabic "MMXVI"

REFAL

$ENTRY Go {
    = <Prout <RomanDecode 'MCMXC'>>
      <Prout <RomanDecode 'MMVIII'>>
      <Prout <RomanDecode 'MDCLXVI'>>;
};

RomanDecode {
    = 0;
    e.D, <Upper e.D>: {
        'M'  e.R = <+ 1000 <RomanDecode e.R>>;
        'CM' e.R = <+  900 <RomanDecode e.R>>;
        'D'  e.R = <+  500 <RomanDecode e.R>>;
        'CD' e.R = <+  400 <RomanDecode e.R>>;
        'C'  e.R = <+  100 <RomanDecode e.R>>;
        'XC' e.R = <+   90 <RomanDecode e.R>>;
        'L'  e.R = <+   50 <RomanDecode e.R>>;
        'XL' e.R = <+   40 <RomanDecode e.R>>;
        'X'  e.R = <+   10 <RomanDecode e.R>>;
        'IX' e.R = <+    9 <RomanDecode e.R>>;
        'V'  e.R = <+    5 <RomanDecode e.R>>;
        'IV' e.R = <+    4 <RomanDecode e.R>>;
        'I'  e.R = <+    1 <RomanDecode e.R>>;
    };
};
Output:
1990
2008
1666

REXX

version 1

Translation of: NetRexx
Works with: Regina
Works with: ooRexx
/* Rexx */

Do
        /* 1990  2008   1666    */
  years = 'MCMXC MMVIII MDCLXVI'

  Do y_ = 1 to words(years)
    Say right(word(years, y_), 10) || ':' decode(word(years, y_))
    End y_

  Return
End
Exit

decode:
  Procedure
Do
  Parse upper arg roman .
  
  If verify(roman, 'MDCLXVI') = 0 then Do

    /* always insert the value of the least significant numeral */
    decnum = rchar(substr(roman, length(roman), 1))
    Do d_ = 1 to length(roman) - 1
      If rchar(substr(roman, d_, 1)) < rchar(substr(roman, d_ + 1, 1)) then Do
        /* Handle cases where numerals are not in descending order */
        /*   subtract the value of the numeral */
        decnum = decnum - rchar(substr(roman, d_, 1))
        End
      else Do
        /* Normal case */
        /*   add the value of the numeral */
        decnum = decnum + rchar(substr(roman, d_, 1))
        End
      End d_
    End
  else Do
    decnum = roman 'contains invalid roman numerals'
    End

  Return decnum
End
Exit

rchar:
  Procedure
Do
  Parse upper arg ch +1 .

  select 
    when ch = 'M' then digit = 1000
    when ch = 'D' then digit =  500
    when ch = 'C' then digit =  100
    when ch = 'L' then digit =   50
    when ch = 'X' then digit =   10
    when ch = 'V' then digit =    5
    when ch = 'I' then digit =    1
    otherwise          digit =    0
    end

  Return digit
End
Exit
Output:
     MCMXC: 1990
    MMVIII: 2008
   MDCLXVI: 1666

version 2

This version of the (above) REXX program:

  •   removes 3 sets of superfluous   do──end   statements
  •   removes dead code (3 REXX statements that can't be executed)
  •   replaced   substr(xxx, length(xxx), 1)     with     right(xxx,1)
  •   removes a useless   parse   statement
  •   compresses 63 lines to 29 lines
  •   reordered   if   statements by most likely to occur

This REXX version   won't   handle:

  •   Roman numbers like   IIXX
  •   the   j   and   u   numerals
  •   (deep) parenthesis type Roman numbers
/*REXX program  converts  Roman numeral number(s)  ───►  Arabic numerals  (or numbers). */
rYear = 'MCMXC'    ;      say right(rYear, 9)":"     rom2dec(rYear)
rYear = 'mmviii'   ;      say right(rYear, 9)":"     rom2dec(rYear)
rYear = 'MDCLXVI'  ;      say right(rYear, 9)":"     rom2dec(rYear)
exit                                             /*stick a fork in it,  we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
rom2dec: procedure;  arg roman .                 /*obtain the Roman numeral number.     */
if verify(roman, 'MDCLXVI')\==0  then return  "***error***  invalid Roman number:"   roman
#=rChar(right(roman, 1))                         /*start with the last Roman numeral.   */
            do j=1  for length(roman) - 1
            x=rChar( substr(roman, j  , 1) )     /*extract the  current  Roman numeral. */
            y=rChar( substr(roman, j+1, 1) )     /*extract the     next  Roman numeral. */
            if x<y  then # = #-x                 /*Is   x<y ?         Then subtract it. */
                    else # = #+x                 /*Is   x≥y ?           "    add     "  */
            end   /*j*/
return #
/*──────────────────────────────────────────────────────────────────────────────────────*/
rChar: procedure;  arg _                         /*convert Roman number to Arabic digits*/
       if _=='I'  then return    1
       if _=='V'  then return    5
       if _=='X'  then return   10
       if _=='L'  then return   50
       if _=='C'  then return  100
       if _=='D'  then return  500
       if _=='M'  then return 1000
                       return    0               /*indicate an  invalid  Roman numeral. */

version 3

This REXX version allows the use of   j   which was being used in the later part of the Holy Roman Empire
(as a trailing   i   in Roman numerals).

Also, this program converts   IIXX   correctly.     (Note:   this Roman numeral was actually chiseled on
some Roman monuments, archways, and tombs/crypts.)

Also supported are larger numbers such as   (M)   which is a Roman numeral(s) within a set of grouping
symbols,   in this case, a set of parenthesis   (brackets and/or braces can also be used).

Deep parentheses are also supported:   (MM)   is two million,   ((MMM))   is three billion.

Normally, the Romans used an overbar (vinculum) for larger numbers (such as   XL   for forty-thousand),
but the use of such a character is very problematic for computers to deal with,   so parenthesis are used
instead.

The Romans also had symbols for some fractions which would be a good addition to this task.
Also, lowercase   u   was also used for lowercase   v
Also note that   IIII   is a legal Roman numeral construct;   (as demonstrated by almost any old clock or
"dialed" wristwatch that has Roman numerals).

/*REXX program  converts  Roman numeral number(s)  ───►  Arabic numerals  (or numbers). */
numeric digits 1000                              /*so we can handle the big numbers.    */
parse arg z                                      /*obtain optional arguments from the CL*/
if z=''  then z= "MCMXC mmviii IIXX LU MDCLXVI MDWLXVI ((mmm)) [[[[[D]]]]]"  /*defaults.*/

     do j=1  for words(z);   y=word(z, j)        /*process each of the Roman numbers.   */
     say  right(y, 20)':'    rom2dec(y)          /*display original and decimal version.*/
     end   /*j*/
exit                                             /*stick a fork in it,  we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
rom2dec:  procedure;   h='0'x;   #=0;   $=1;   arg n .         /*"ARG"  uppercases  N.  */
n=translate(n, '()()', "[]{}");  _=verify(n, 'MDCLXVUIJ()')    /*trans grouping symbols.*/
if _\==0  then return '***error*** invalid Roman numeral:'  substr(n,_,1)   /*tell error*/
@.=1; @.m=1000; @.d=500; @.c=100; @.l=50; @.x=10; @.u=5; @.v=5 /*Roman numeral values.  */
                                                               /* [↓]  convert number.  */
   do k=length(n)  to 1  by -1;  _=substr(n, k, 1)             /*examine a Roman numeral*/
                                                               /* [↑]  scale up or down.*/
   if _=='(' | _==")"  then  do;  $=$*1000; if _=='(' then $=1 /* (≡scale ↑;  )≡scale ↓ */
                                  iterate                      /*go & process next digit*/
                             end
   _=@._*$                                                     /*scale it if necessary. */
   if _>h  then h=_                                            /*remember Roman numeral.*/
   if _<h  then #=#-_                                          /*char>next?  Then sub.  */
           else #=#+_                                          /*            else add.  */
   end   /*k*/
return #                                                       /*return Arabic number.  */

output   when using the default inputs:

               MCMXC: 1990
              mmviii: 2008
                IIXX: 18
                  LU: 55
             MDCLXVI: 1666
             MDWLXVI: ***error*** invalid Roman numeral: W
             ((mmm)): 3000000000
         [[[[[D]]]]]: 500000000000000000

Ring

symbols = "MDCLXVI"
weights = [1000,500,100,50,10,5,1]

see "MCMXCIX = " + romanDec("MCMXCIX") + nl
see "MDCLXVI =" + romanDec("MDCLXVI") + nl
see "XXV = " + romanDec("XXV") + nl
see "CMLIV = " + romanDec("CMLIV") + nl
see "MMXI = " + romanDec("MMXI") + nl

func romanDec roman
     n = 0
     lastval = 0
     arabic = 0
     for i = len(roman) to 1 step -1
         n = substr(symbols,roman[i])
         if n > 0 n = weights[n] ok
         if n < lastval arabic = arabic - n
         else arabic = arabic + n ok
         lastval = n
     next
     return arabic

RPL

Works with: Halcyon Calc version 4.2.7
RPL code Comment
≪ DUP SIZE "IVXLCDM" { 1 5 10 50 100 500 1000 } 
  → rom siz dig val
  ≪ 0 1 siz FOR j 
       rom j DUP SUB 
       IF dig SWAP POS THEN val LAST GET END 
       IF DUP2 < THEN SWAP NEG SWAP END
    NEXT 
    0 1 siz START + NEXT +
≫ ≫ 'ROM→' STO
ROM→ ( "ROMAN" -- n )
store input string, length and tables
scan string from highest digit
   get jth character
   if char in the table then push its value into stack
   if > to previous value then change sign of previous value
   
   sum the stack
.

Ruby

def fromRoman(roman)
  r = roman.upcase
  n = 0
  until r.empty? do
    case
    when r.start_with?('M')  then v = 1000; len = 1
    when r.start_with?('CM') then v = 900;  len = 2
    when r.start_with?('D')  then v = 500;  len = 1
    when r.start_with?('CD') then v = 400;  len = 2
    when r.start_with?('C')  then v = 100;  len = 1
    when r.start_with?('XC') then v = 90;   len = 2
    when r.start_with?('L')  then v = 50;   len = 1
    when r.start_with?('XL') then v = 40;   len = 2
    when r.start_with?('X')  then v = 10;   len = 1
    when r.start_with?('IX') then v = 9;    len = 2
    when r.start_with?('V')  then v = 5;    len = 1
    when r.start_with?('IV') then v = 4;    len = 2
    when r.start_with?('I')  then v = 1;    len = 1
    else
      raise ArgumentError.new("invalid roman numerals: " + roman)
    end
    n += v
    r.slice!(0,len)
  end
  n
end

[ "MCMXC", "MMVIII", "MDCLXVI" ].each {|r| p r => fromRoman(r)}
Output:
{"MCMXC"=>1990}
{"MMVIII"=>2008}
{"MDCLXVI"=>1666}

or

SYMBOLS = [ ['M', 1000], ['CM', 900], ['D', 500], ['CD', 400], ['C', 100], ['XC', 90],
            ['L', 50], ['XL', 40], ['X', 10], ['IX', 9], ['V', 5], ['IV', 4], ['I', 1] ]

def parseRoman(roman)
  r = roman.upcase
  n = 0
  SYMBOLS.each { |sym, val| n += val while r.sub!(/^#{sym}/, "") }
  n
end

[ "MCMXC", "MMVIII", "MDCLXVI" ].each {|r| puts "%8s :%5d" % [r, parseRoman(r)]}
Output:
   MCMXC : 1990
  MMVIII : 2008
 MDCLXVI : 1666

Rust

struct RomanNumeral {
    symbol: &'static str,
    value: u32
}

const NUMERALS: [RomanNumeral; 13] = [
    RomanNumeral {symbol: "M",  value: 1000},
    RomanNumeral {symbol: "CM", value: 900},
    RomanNumeral {symbol: "D",  value: 500},
    RomanNumeral {symbol: "CD", value: 400},
    RomanNumeral {symbol: "C",  value: 100},
    RomanNumeral {symbol: "XC", value: 90},
    RomanNumeral {symbol: "L",  value: 50},
    RomanNumeral {symbol: "XL", value: 40},
    RomanNumeral {symbol: "X",  value: 10},
    RomanNumeral {symbol: "IX", value: 9},
    RomanNumeral {symbol: "V",  value: 5},
    RomanNumeral {symbol: "IV", value: 4},
    RomanNumeral {symbol: "I",  value: 1}
];

fn to_hindu(roman: &str) -> u32 {
    match NUMERALS.iter().find(|num| roman.starts_with(num.symbol)) {
        Some(num) => num.value + to_hindu(&roman[num.symbol.len()..]),
        None => 0, // if string empty, add nothing
    }
}

fn main() {
    let roms = ["MMXIV", "MCMXCIX", "XXV", "MDCLXVI", "MMMDCCCLXXXVIII"];
    for &r in &roms {
        // 15 is minimum formatting width of the first argument, there for alignment
        println!("{:2$} = {}", r, to_hindu(r), 15);
    }
}
Output:
MMXIV           = 2014
MCMXCIX         = 1999
XXV             = 25
MDCLXVI         = 1666
MMMDCCCLXXXVIII = 3888

Scala

def fromRoman( r:String ) : Int = {
  val arabicNumerals = List("CM"->900,"M"->1000,"CD"->400,"D"->500,"XC"->90,"C"->100,
                            "XL"->40,"L"->50,"IX"->9,"X"->10,"IV"->4,"V"->5,"I"->1)	
                            
  var s = r                          
  arabicNumerals.foldLeft(0){ (n,t) => { 
    val l = s.length; s = s.replaceAll(t._1,""); val c = (l - s.length)/t._1.length  // Get the frequency
    n + (c*t._2)  // Add the arabic numerals up  
  } }
}

// Here is a another version that does a simple running sum:
def fromRoman2(s: String) : Int = {
    val numerals = Map('I' -> 1, 'V' -> 5, 'X' -> 10, 'L' -> 50, 'C' -> 100, 'D' -> 500, 'M' -> 1000)

    s.toUpperCase.map(numerals).foldLeft((0,0)) {
      case ((sum, last), curr) =>  (sum + curr + (if (last < curr) -2*last else 0), curr) }._1
  }
}

// A small test
def test( roman:String ) = println( roman + " => " + fromRoman( roman ) )
  
test("MCMXC")
test("MMVIII")
test("MDCLXVI")
Output:
MCMXC => 1990
MMVIII => 2008
MDCLXVI => 1666

Scheme

Works with: Gauche Scheme
(use gauche.collection) ;; for fold2

(define (char-val char)
  (define i (string-scan "IVXLCDM" char))
  (* (expt 10 (div i 2)) (expt 5 (mod i 2))))

(define (decode roman)
  (fold2
    (lambda (n sum prev-val)
      (values ((if (< n prev-val) - +) sum n) (max n prev-val)))
    0 0
    (map char-val (reverse (string->list roman)))))

Testing:

(for-each
  (^s (format #t "~7d: ~d\n" s (decode s)))
  '("MCMLVI" "XXC" "MCMXC" "XXCIII" "IIIIX" "MIM" "LXXIIX"))
Output:
 MCMLVI: 1956
    XXC: 80
  MCMXC: 1990
 XXCIII: 83
  IIIIX: 6
    MIM: 1999
 LXXIIX: 78

Seed7

$ include "seed7_05.s7i";

const func integer: ROMAN parse (in string: roman) is func
  result
    var integer: arabic is 0;
  local
    var integer: index is 0;
    var integer: number is 0;
    var integer: lastval is 0;
  begin
    for index range length(roman) downto 1 do
      case roman[index] of
        when {'M', 'm'}: number := 1000;
        when {'D', 'd'}: number :=  500;
        when {'C', 'c'}: number :=  100;
        when {'L', 'l'}: number :=   50;
        when {'X', 'x'}: number :=   10;
        when {'V', 'v'}: number :=    5;
        when {'I', 'i'}: number :=    1;
        otherwise:       raise RANGE_ERROR;
      end case;
      if number < lastval then
        arabic -:= number;
      else
        arabic +:= number;
      end if;
      lastval := number;
    end for;
  end func;

const proc: main is func
  begin
    writeln(ROMAN parse "MCMXC");
    writeln(ROMAN parse "MMVIII");
    writeln(ROMAN parse "MDCLXVI");
  end func;

Original source: [1]

Output:
1990
2008
1666

SenseTalk

function RomanNumeralsDecode numerals
	put {
		"M": 1000,
		"D": 500,
		"C": 100,
		"L": 50,
		"X": 10,
		"V": 5,
		"I": 1
	} into values
	
	put 0 into total
	repeat with each character letter of numerals
		if values.(character the counter + 1 of numerals) is less than or equal to values.(letter) 
			add values.(letter) to total
		else
			subtract values.(letter) from total
		end if
	end repeat
	return total
end RomanNumeralsDecode
repeat for each item in [
	"MCMXC",
	"MMVIII",
	"MDCLXVI",
]
	put RomanNumeralsDecode(it)
end repeat
Output:
1990
2008
1666

Sidef

func roman2arabic(roman) {
 
    var arabic = 0
    var last_digit = 1000
 
    static m = Hash(
        I =>    1,
        V =>    5,
        X =>   10,
        L =>   50,
        C =>  100,
        D =>  500,
        M => 1000,
    )
 
    roman.uc.chars.map{m{_} \\ 0}.each { |digit|
        if (last_digit < digit) {
            arabic -= (2 * last_digit)
        }
        arabic += (last_digit = digit)
    }
 
    return arabic
}
 
%w(MCMXC MMVIII MDCLXVI).each { |roman_digit|
    "%-10s == %d\n".printf(roman_digit, roman2arabic(roman_digit))
}
Output:
MCMXC      == 1990
MMVIII     == 2008
MDCLXVI    == 1666

Simpler solution:

func roman2arabic(digit) {
    digit.uc.trans([
        :M:  '1000+',
        :CM:  '900+',
        :D:   '500+',
        :CD:  '400+',
        :C:   '100+',
        :XC:   '90+',
        :L:    '50+',
        :XL:   '40+',
        :X:    '10+',
        :IX:    '9+',
        :V:     '5+',
        :IV:    '4+',
        :I:     '1+',
    ]).split('+').map{.to_i}.sum;
}
 
%w(MCMXC MMVIII MDCLXVI).each { |roman_num|
    say "#{roman_num}\t-> #{roman2arabic(roman_num)}";
}
Output:
MCMXC   -> 1990
MMVIII  -> 2008
MDCLXVI -> 1666

Simula

BEGIN

    INTEGER PROCEDURE FROMROMAN(S); TEXT S;
    BEGIN
        PROCEDURE P(INTVAL, NUM); INTEGER INTVAL; TEXT NUM;
        BEGIN
            INTEGER NLEN;
            NLEN := NUM.LENGTH;
            WHILE INDEX + NLEN - 1 <= SLEN AND THEN
                S.SUB(INDEX, NLEN) = NUM DO
            BEGIN
                RESULT := RESULT + INTVAL;
                INDEX := INDEX + NLEN;
            END WHILE;
        END P;
        INTEGER RESULT, INDEX, SLEN;
        SLEN := S.LENGTH;
        INDEX := 1;
        P( 1000, "M"  );
        P(  900, "CM" );
        P(  500, "D"  );
        P(  400, "CD" );
        P(  100, "C"  );
        P(   90, "XC" );
        P(   50, "L"  );
        P(   40, "XL" );
        P(   10, "X"  );
        P(    9, "IX" );
        P(    5, "V"  );
        P(    4, "IV" );
        P(    1, "I"  );
        FROMROMAN := RESULT;
    END FROMROMAN;

    TEXT T;
    FOR T :- "MCMXC", "MMVIII", "MDCLXVI" DO
    BEGIN
        OUTTEXT("ROMAN """);
        OUTTEXT(T);
        OUTTEXT(""" => ");
        OUTINT(FROMROMAN(T), 0);
        OUTIMAGE;
    END FOR;

END PROGRAM;
Output:
ROMAN "MCMXC" => 1990
ROMAN "MMVIII" => 2008
ROMAN "MDCLXVI" => 1666

SNOBOL4

*        Roman to Arabic
        define('arabic(n)s,ch,val,sum,x') :(arabic_end)
arabic  s = 'M1000 D500 C100 L50 X10 V5 I1 '
        n = reverse(n)
arab1   n len(1) . ch = :f(arab2)
        s ch break(' ') . val
        val = lt(val,x) (-1 * val)
        sum = sum + val; x = val        :(arab1)
arab2   arabic = sum                    :(return)
arabic_end        

*        Test and display
        tstr = 'MMX MCMXCIX MCDXCII MLXVI CDLXXVI "
tloop   tstr break(' ') . r span(' ') = :f(out)
        astr = astr r '=' arabic(r) ' ' :(tloop)
out     output = astr
end
Output:
MMX=2010 MCMXCIX=1999 MCDXCII=1492 MLXVI=1066 CDLXXVI=476

Here's an alternative version, which is maybe more SNOBOL4-idiomatic and less like one might program it in a more common language:

*   Roman to Arabic
	define("arabic1(romans,arabic1)rdigit,adigit,b4")
	romans1 = " 0 IX9 IV4 III3 II2 I1 VIII8 VII7 VI6 V5"  :(arabic1_end)
arabic1 ident(romans)                             :s(return)
	romans (break("IV") | rem) . b4 rem . rdigit = b4   
        romans1 " " rdigit any("0123456789") . adigit 
	arabic1 = adigit arabic1
        romans = replace(romans,"MDCLX","CLXVI")  :(arabic1)
arabic1_end
*   Test and display
        tstr = "MMX MCMXCIX MCDXCII MLXVI CDLXXVI "
tloop   tstr break(' ') . r span(' ') =           :f(out)
        astr = astr r '=' arabic1(r) ' '          :(tloop)
out     output = astr
end

The output is the same as in the earlier version.

The following version takes advantage of some of the so-called "SPITBOL extensions", which are to be found in most modern implementations. This allows removing several labels and explicit transfers of control, and moves some of the looping into the pattern matcher. Again, the output is the same.

*   Roman to Arabic
	define("arabic1(romans,arabic1)rdigit,adigit,b4")
	romans1 = " 0 IX9 IV4 III3 II2 I1 VIII8 VII7 VI6 V5"  :(arabic1_end)
arabic1 ident(romans)                             :s(return)
	romans (break("IV") | rem) . b4 rem . rdigit = replace(b4,"MDCLX","CLXVI")     
        romans1 " " rdigit any("0123456789") . adigit 
	arabic1 = adigit arabic1                  :(arabic1)
arabic1_end
*   Test and display
        tstr = " MMX MCMXCIX MCDXCII MLXVI CDLXXVI "
        tstr span(' ') break(' ') $ r *?(astr = astr r '=' arabic1(r) ' ') fail
        output = astr
end

SPL

r2a(r)=
  n = [1,5,10,50,100,500,1000]
  a,m = 0
  > i, #.size(r)..1, -1
    v,c = n[#.pos("IVXLCDM",#.mid(r,i))]
    ? v<m, v = -v
    ? c>m, m = c
    a += v
  <
  <= a
.

t = ["MMXI","MIM","MCMLVI","MDCLXVI","XXCIII","LXXIIX","IIIIX"]
> i, 1..#.size(t,1)
  #.output(t[i]," = ",r2a(t[i]))
<
Output:
MMXI = 2011
MIM = 1999
MCMLVI = 1956
MDCLXVI = 1666
XXCIII = 83
LXXIIX = 78
IIIIX = 6

Swift

extension Int {
    init(romanNumerals: String) {
        let values = [
            ( "M", 1000),
            ("CM",  900),
            ( "D",  500),
            ("CD",  400),
            ( "C",  100),
            ("XC",   90),
            ( "L",   50),
            ("XL",   40),
            ( "X",   10),
            ("IX",    9),
            ( "V",    5),
            ("IV",    4),
            ( "I",    1),
        ]
        
        self = 0
        var raw = romanNumerals
        for (digit, value) in values {
            while raw.hasPrefix(digit) {
                self += value
                raw.removeFirst(digit.count)
            }
        }
    }
}
Output:
Int(romanNumerals: "MDCLXVI") // 1666

Tailspin

def digits: [(M:1000"1"), (CM:900"1"), (D:500"1"), (CD:400"1"), (C:100"1"), (XC:90"1"), (L:50"1"), (XL:40"1"), (X:10"1"), (IX:9"1"), (V:5"1"), (IV:4"1"), (I:1"1")];
composer decodeRoman
  @: 1;
  [ <digit>* ] -> \(@: 0"1"; $... -> @: $@ + $; $@ !\)
  rule digit: <value>* (@: $@ + 1;)
  rule value: <='$digits($@)::key;'> -> $digits($@)::value
end decodeRoman

'MCMXC' -> decodeRoman -> !OUT::write
'
' -> !OUT::write
'MMVIII' -> decodeRoman -> !OUT::write
'
' -> !OUT::write
'MDCLXVI' -> decodeRoman -> !OUT::write
Output:
1990"1"
2008"1"
1666"1"

Tcl

As long as we assume that we have a valid roman number, this is most easily done by transforming the number into a sum and evaluating the expression:

proc fromRoman rnum {
    set map {M 1000+ CM 900+ D 500+ CD 400+ C 100+ XC 90+ L 50+ XL 40+ X 10+ IX 9+ V 5+ IV 4+ I 1+}
    expr [string map $map $rnum]0}
}

Demonstrating:

foreach r {MCMXC MDCLXVI MMVIII} {
    puts "$r\t-> [fromRoman $r]"
}
Output:
MCMXC	-> 1990
MDCLXVI	-> 1666
MMVIII	-> 2008

TMG

Unix TMG dialect. Version without validation:

loop:   parse(roman)\loop;
roman:  string(!<<MDCLXVI>>) [n=0] num
letter: num/render letter;
num:  <M> [n=+1750]
   |  <D> [n=+764]
   |  <C> ( <M> [n=+1604]
          | <D> [n=+620]
          | [n=+144] )
   |  <L> [n=+62]
   |  <X> ( <C> [n=+132]
          | <L> [n=+50]
          | [n=+12] )
   |  <V> [n=+5]
   |  <I> ( <X> [n=+11] 
          | <V> [n=+4]
          | [n++] );
render: decimal(n) = { 1 * };

n:  0;

Unix TMG dialect. Version with validation:

loop:   [wsz = &a - &n]
        parse(line)\loop
        parse(error)\loop;
line:   roman *;
roman:  [n=0] [off=0]
        comb((<^>),(<&>),(<M>))
        comb((<M>),(<D>),(<C>))
        comb((<C>),(<L>),(<X>))
        comb((<X>),(<V>),(<I>))
        [n>0?] decimal(n) = { 1 * };
comb:   proc(c1,c2,c3)
        [v1 = *(wsz*off++ + &a)]
        [v2 = *(wsz*off++ + &a)]
        [v3 = *(wsz*off   + &a)]
        ( c3 ( c3 ( c3 [n=+(3*v3)]
                  | [n=+(2*v3)] )
             | c1 [v1>0?] [n=+v1-v3]
             | c2 [v2>0?] [n=+v2-v3]
             | [n=+v3] )
        | c2 [v2>0?] [n=+v2]
             ( c3 ( c3 ( c3 [n=+(3*v3)]
                       | [n=+(2*v3)] )
                  | [n=+v3] )
             | () )
        | () );
error:  smark ignore(<<>>) string(notnewline) scopy *
        = { <error: > 1 * };

notnewline: !<<
>>;

n:  0;
a:  0; 0; 1750; 764; 144; 62; 12; 5; 1;
off:0;
wsz:0;
v1: 0; v2: 0; v3: 0;

Sample input:

MMXVI
LV
XII
MCMLIV
IIXX
IM
XXCIII

Sample output:

2016
55
12
1954
error: IIXX
error: IM
error: XXCIII

TUSCRIPT

$$ MODE TUSCRIPT
LOOP roman_number="MCMXC'MMVIII'MDCLXVI"
arab_number=DECODE (roman_number,ROMAN)
PRINT "Roman number ",roman_number," equals ", arab_number
ENDLOOP
Output:
Roman number MCMXC equals 1990
Roman number MMVIII equals 2008
Roman number MDCLXVI equals 1666

UNIX Shell

#!/bin/bash

roman_to_dec() {
  local rnum=$1
  local n=0
  local prev=0

  for ((i=${#rnum}-1;i>=0;i--))
  do
    case "${rnum:$i:1}" in
    M)  a=1000 ;;
    D)  a=500 ;;
    C)  a=100 ;;
    L)  a=50 ;;
    X)  a=10 ;;
    V)  a=5 ;;
    I)  a=1 ;;
    esac

    if [[ $a -lt $prev ]]
    then
       let n-=a
    else
       let n+=a
    fi

    prev=$a
  done

  echo "$rnum = $n"
}

roman_to_dec MCMXC
roman_to_dec MMVIII
roman_to_dec MDCLXVI

VBA

Convert Romans (i.e : XVI) in integers

Option Explicit

Sub Main_Romans_Decode()
Dim Arr(), i&

    Arr = Array("III", "XXX", "CCC", "MMM", "VII", "LXVI", "CL", "MCC", "IV", "IX", "XC", "ICM", "DCCCXCIX", "CMI", "CIM", "MDCLXVI", "MCMXC", "MMXVII")
    For i = 0 To UBound(Arr)
        Debug.Print Arr(i) & "   >>> " & lngConvert(CStr(Arr(i)))
    Next
End Sub

Function Convert(Letter As String) As Long
Dim Romans(), DecInt(), Pos As Integer

    Romans = Array("M", "D", "C", "L", "X", "V", "I")
    DecInt = Array(1000, 500, 100, 50, 10, 5, 1)
    Pos = -1
    On Error Resume Next
    Pos = Application.Match(Letter, Romans, 0) - 1
    On Error GoTo 0
    If Pos <> -1 Then Convert = DecInt(Pos)
End Function

Function lngConvert(strRom As String) 'recursive function
Dim i As Long, iVal As Integer

    If Len(strRom) = 1 Then
        lngConvert = Convert(strRom)
    Else
        iVal = Convert(Mid(strRom, 1, 1))
        If iVal < Convert(Mid(strRom, 2, 1)) Then iVal = iVal * (-1)
        lngConvert = iVal + lngConvert(Mid(strRom, 2, Len(strRom) - 1))
    End If
End Function
Output:
III   >>> 3
XXX   >>> 30
CCC   >>> 300
MMM   >>> 3000
VII   >>> 7
LXVI   >>> 66
CL   >>> 150
MCC   >>> 1200
IV   >>> 4
IX   >>> 9
XC   >>> 90
ICM   >>> 899
DCCCXCIX   >>> 899
CMI   >>> 901
CIM   >>> 1099
MDCLXVI   >>> 1666
MCMXC   >>> 1990
MMXVII   >>> 2017

VBScript

Translation of: 360 Assembly
' Roman numerals Encode - Visual Basic - 18/04/2019

Function toRoman(ByVal value)
    Dim arabic
    Dim roman
    Dim i, result
    arabic = Array(1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1)
    roman = Array("M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I")
    For i = 0 To 12
        Do While value >= arabic(i)
            result = result + roman(i)
            value = value - arabic(i)
        Loop
    Next 'i
    toRoman = result
End Function 'toRoman
 
    n=InputBox("Number, please","Roman numerals/Encode")
    code=MsgBox(n & vbCrlf & toRoman(n),vbOKOnly+vbExclamation,"Roman numerals/Encode")
	If code=vbOK Then ok=1
Output:
III   >>> 3
XXX   >>> 30
CCC   >>> 300
MMM   >>> 3000
VII   >>> 7
LXVI   >>> 66
CL   >>> 150
MCC   >>> 1200
IV   >>> 4
IX   >>> 9
XC   >>> 90
ICM   >>> 901
DCCCXCIX   >>> 899
CMI   >>> 901
CIM   >>> 1099
MDCLXVI   >>> 1666
MCMXC   >>> 1990
MMXVII   >>> 2017
I   >>> 1
XIV   >>> 14
MMMDCCCLXXXVIII   >>> 3888
MMMCMXCIX   >>> 3999

Vedit macro language

// Main program for testing the function
//
do {
    Get_Input(10, "Enter a roman numeral: ", NOCR+STATLINE)
    Call("Roman_to_Arabic")
    Reg_Type(10) Message("	= ") Num_Type(#1)
} while(#1)
Return

// Convert Roman numeral into numeric value
//  in:  @10 = Roman numeral
//  out: #1  = numeric value
//
:Roman_to_Arabic:
    Buf_Switch(Buf_Free)
    Ins_Text("M1000 D500 C100 L50 X10 V5 I1") Ins_Newline
    Reg_Ins(10) Ins_Char(' ')
    #1 = #2 = 0

    Repeat(ALL) {
        #3 = #2                                  // #3 = previous character
        Goto_Line(2)                             // roman numeral to be converted
        if (At_EOL) {
            Break                                // all done
        }
        Reg_Copy_Block(11, CP, CP+1, DELETE)     // next character in roman numeral
        if (Search(@11, BEGIN+ADVANCE+NOERR)) {  // find character from the table
            #2 = Num_Eval(SUPPRESS)              // corresponding numeric value
            if (#2 > #3) {                       // larger than previous digit?
                #1 -= #3                         // substract previous digit
            } else {
                #1 += #3                         // add previous digit
            }
        }
    }
    Reg_Empty(11)
    Buf_Quit(OK)
Return
Output:
iv	=     4
xii	=    12
MDCLXVI	=  1666
MCMXC	=  1990
MMXI	=  2011

V (Vlang)

Translation of: Kotlin
const romans = ["I", "III", "IV", "VIII", "XLIX", "CCII", "CDXXXIII", "MCMXC", "MMVIII", "MDCLXVI"]

fn main() {
    for roman in romans {println("${roman:-10} = ${roman_decode(roman)}")}
}

fn roman_decode(roman string) int {
    mut n := 0
    mut last := "O"
	if roman =="" {return n}
    for c in roman { 
        match c.ascii_str() {
            "I" {n++}   
            "V" {if last == "I" {n += 3}  else {n += 5}}
            "X" {if last == "I" {n += 8}   else {n += 10}}
            "L" {if last == "X" {n += 30}  else {n += 50}}
            "C" {if last == "X" {n += 80}  else {n += 100}}
            "D" {if last == "C" {n += 300} else {n += 500}}
            "M" {if last == "C" {n += 800} else {n += 1000}}
			else {last = c.ascii_str()}
        }
    }
    return n
}
Output:
I          = 1
III        = 3
IV         = 4
VIII       = 8
XLIX       = 49
CCII       = 202
CDXXXIII   = 433
MCMXC      = 1990
MMVIII     = 2008
MDCLXVI    = 1666

Wren

Translation of: Kotlin
Library: Wren-fmt
import "./fmt" for Fmt

var decode = Fn.new { |r|
    if (r == "") return 0
    var n = 0
    var last = "0"
    for (c in r) {
        var k
        if (c == "I") {
            k = 1
        } else if (c == "V") {
            k = (last == "I") ? 3 : 5
        } else if (c == "X") {
            k = (last == "I") ? 8 : 10
        } else if (c == "L") {
            k = (last == "X") ? 30 : 50
        } else if (c == "C") {
            k = (last == "X") ? 80 : 100
        } else if (c == "D") {
            k = (last == "C") ? 300 : 500
        } else if (c == "M") {
            k = (last == "C") ? 800 : 1000
        }
        n = n + k
        last = c
    }
    return n
}

var romans = ["I", "III", "IV", "VIII", "XLIX", "CCII", "CDXXXIII", "MCMXC", "MMVIII", "MDCLXVI"]
for (r in romans) System.print("%(Fmt.s(-10, r)) = %(decode.call(r))")
Output:
I          = 1
III        = 3
IV         = 4
VIII       = 8
XLIX       = 49
CCII       = 202
CDXXXIII   = 433
MCMXC      = 1990
MMVIII     = 2008
MDCLXVI    = 1666

XLISP

Uses basic list processing and recursion. Probably not amazingly fast, but quite concise and hopefully clear.

(defun decode (r)
    (define roman '((#\m 1000) (#\d 500) (#\c 100) (#\l 50) (#\x 10) (#\v 5) (#\i 1)))
    (defun to-arabic (rn rs a)
        (cond
            ((null rn) a)
            ((eqv? (car rn) (caar rs)) (to-arabic (cdr rn) roman (if (and (not (eqv? (car rn) (cadr rn))) (< (cadar rs) (to-arabic (cdr rn) roman 0)))
                (- a (cadar rs))
                (+ a (cadar rs)) ) ) )
            (t (to-arabic rn (cdr rs) a)) ) )
    (to-arabic (string->list r) roman 0) )

Test it in a REPL:

[1] (mapcar decode '("mcmxc" "mmviii" "mdclxvi"))

(1990 2008 1666)

XPL0

string 0;                       \use zero-terminated strings
code CrLf=9, IntOut=11;

func Roman(Str);                \Convert Roman numeral string to decimal value
char Str;
int  I, Val, Val0, Sum;
[I:= 0;  Sum:= 0;  Val0:= 5000;
loop    [case Str(I) of
          ^M: Val:= 1000;
          ^D: Val:= 500;
          ^C: Val:= 100;
          ^L: Val:= 50;
          ^X: Val:= 10;
          ^V: Val:= 5;
          ^I: Val:= 1
        other return Sum;       \zero string terminator
        I:= I+1;
        Sum:= Sum + Val;
        if Val > Val0 then Sum:= Sum - 2*Val0;
        Val0:= Val;
        ];
];

[IntOut(0, Roman("MCMXC"));    CrLf(0);
 IntOut(0, Roman("MMVIII"));   CrLf(0);
 IntOut(0, Roman("MDCLXVI"));  CrLf(0);
]
Output:
1990
2008
1666

XQuery

xquery version "3.1";

declare function local:decode-roman-numeral($roman-numeral as xs:string) {
    $roman-numeral
    => upper-case()
    => for-each( 
        function($roman-numeral-uppercase) { 
            analyze-string($roman-numeral-uppercase, ".")/fn:match 
            ! map { "M": 1000, "D": 500, "C": 100, "L": 50, "X": 10, "V": 5, "I": 1 }(.) 
        } 
    )
    => fold-right([0,0], 
        function($number as xs:integer, $accumulator as array(*)) { 
            let $running-total := $accumulator?1
            let $previous-number := $accumulator?2
            return
                if ($number lt $previous-number) then
                    [ $running-total - $number, $number ]
                else
                    [ $running-total + $number, $number ]
        }
    )
    => array:get(1)
};

let $roman-numerals :=
    map {
        "MCMXCIX": 1999,
        "MDCLXVI": 1666,
        "XXV": 25,
        "XIX": 19,
        "XI": 11,
        "CMLIV": 954,
        "MMXI": 2011,
        "CD": 400,
        "MCMXC": 1990,
        "MMVIII": 2008,
        "MMIX": 2009,
        "MMMDCCCLXXXVIII": 3888 
    }
return
    map:for-each(
        $roman-numerals, 
        function($roman-numeral, $expected-value) { 
            local:decode-roman-numeral($roman-numeral) eq $expected-value
        }
    )

zkl

var romans = L(
   L("M", 1000), L("CM", 900), L("D",  500), L("CD", 400), L("C",  100),
   L("XC",  90), L("L",   50), L("XL",  40), L("X",   10), L("IX",   9),
   L("V",    5), L("IV",   4), L("I",    1));
fcn toArabic(romanNumber){	// romanNumber needs to be upper case
   if (not RegExp("^[CDILMVX]+$").matches(romanNumber))
      throw(Exception.ValueError("Not a Roman number: %s".fmt(romanNumber)));
   reg value = 0;
   foreach R,N in (romans){
      while (0 == romanNumber.find(R)){
	 value += N;
	 romanNumber = romanNumber[R.len(),*];
      }
   }
   return(value);
}
toArabic("MCMXC")   //-->1990
toArabic("MMVIII")  //-->2008
toArabic("MDCLXVI") //-->1666

Zoea

program: roman_decimal
  input: 'XIII'
  output: 13

Zoea Visual

Roman numerals decode

zsh

#!/bin/zsh
function parseroman () {
  local max=0 sum i j
  local -A conv
  conv=(I 1 V 5 X 10 L 50 C 100 D 500 M 1000)
  for j in ${(Oas::)1}; do
    i=conv[$j]
    if (( i >= max )); then
      (( sum+=i ))
      (( max=i ))
    else
      (( sum-=i ))
    fi
  done
  echo $sum
}

parseroman MCMXC
parseroman MMVIII
parseroman MDCLXVI