Roman numerals/Decode

From Rosetta Code
Revision as of 17:48, 6 June 2011 by 79.30.42.93 (talk) (Small changes to the D version)
Task
Roman numerals/Decode
You are encouraged to solve this task according to the task description, using any language you may know.

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 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.

D

<lang d>import std.regex, std.algorithm;

int toArabic(string s) {

   static const weights = [1000, 900, 500, 400, 100, 90, 50,
                           40, 10, 9, 5, 4, 1];
   static /*const*/ symbols = ["M","CM","D","CD","C","XC",
                               "L","XL","X","IX","V","IV","I"];
   int arabic;
   foreach (m; match(s, "CM|CD|XC|XL|IX|IV|[MDCLXVI]"))
       arabic += weights[symbols.countUntil(m.hit)];
   return arabic;

}

void main() {

   assert(toArabic("MCMXC") == 1990);
   assert(toArabic("MMVIII") == 2008);
   assert(toArabic("MDCLXVI") == 1666);

}</lang>

Fortran

Works with: Fortran version 90 and later

<lang fortran>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</lang>

Output

        1990        2008        1666

Icon and Unicon

<lang Icon>link numbers

procedure main() every R := "MCMXC"|"MDCLXVI"|"MMVIII" do

  write(R, " = ",unroman(R))

end</lang>

numbers.icn provides unroman

The code for this procedure is copied below:<lang Icon>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</lang>

Output:

MCMXC = 1990
MDCLXVI = 1666
MMVIII = 2008

J

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

Example use:

<lang j> rom2d 'MCMXC' 1990

  rom2d 'MDCLXVI'

1666

  rom2d 'MMVIII'

2008</lang>

Java

<lang java>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 } }</lang> Output:

1990
2008
1666

Lua

<lang 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" ) )</lang>

1990
2008
1666

PARI/GP

<lang parigp>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

};</lang>

PicoLisp

<lang 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)) ) )</lang>

Test:

: (roman2decimal "MCMXC")
-> 1990

: (roman2decimal "MMVIII")
-> 2008

: (roman2decimal "MDCLXVI")
-> 1666

Prolog

SWI-Prolog and clpfd

Works with SWI-Prolog and library clpfd.
Library clpfd assures that the program works in both managements : Roman towards Arabic and Arabic towards Roman.
It's 99% the same code ! <lang Prolog>roman :- LA = [ _ , 2010, _, 1449, _], LR = ['MDCCLXXXIX', _ , 'CX', _, 'MDCLXVI'], maplist(roman, LA, LR),

% change here ! maplist(my_print,LR, LA).


roman(A, R) :- A #> 0, roman(A, [u, t, h, th], LR, []), label([A]), parse_Roman(CR, LR, []), atom_chars(R, CR).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % using DCG

roman(0, []) --> [].

roman(N, [H | T]) --> {N1 #= N / 10, N2 #= N mod 10}, roman(N1, T), unity(N2, H).

unity(1, u) --> ['I']. unity(1, t) --> ['X']. unity(1, h) --> ['C']. unity(1, th)--> ['M'].

unity(4, u) --> ['IV']. unity(4, t) --> ['XL']. unity(4, h) --> ['CD']. unity(4, th)--> ['MMMM'].

unity(5, u) --> ['V']. unity(5, t) --> ['L']. unity(5, h) --> ['D']. unity(5, th)--> ['MMMMM'].

unity(9, u) --> ['IX']. unity(9, t) --> ['XC']. unity(9, h) --> ['CM']. unity(9, th)--> ['MMMMMMMMM'].

unity(0, _) --> [].


unity(V, U)--> {V #> 5, V1 #= V - 5}, unity(5, U), unity(V1, U).

unity(V, U) --> {V #> 1, V #< 4, V1 #= V-1}, unity(1, U), unity(V1, U).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Extraction of roman "lexeme" parse_Roman(['C','M'|T]) --> ['CM'], parse_Roman(T).

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

parse_Roman(['X','C'| T]) --> ['XC'], parse_Roman(T).


parse_Roman(['X','L'| T]) --> ['XL'], parse_Roman(T).


parse_Roman(['I','X'| T]) --> ['IX'], parse_Roman(T).


parse_Roman(['I','V'| T]) --> ['IV'], parse_Roman(T).

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


parse_Roman([]) --> [].

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % change here ! my_print(A, R) :- format('~w in arabic is ~w~n', [A, R]). </lang> Output :

 ?- roman.
MDCCLXXXIX in arabic is 1789
MMX in arabic is 2010
CX in arabic is 110
MCDXLIX in arabic is 1449
MDCLXVI in arabic is 1666
MCMXCIV in arabic is 1994
true

SWI Prolog

This example is untested. Please check that it's correct, debug it as necessary, and remove this message.


Works with: SWI Prolog

<lang prolog>char_to_num('M', 1000). char_to_num('D', 500). char_to_num('C', 100). char_to_num('L', 50). char_to_num('X', 10). char_to_num('V', 5). char_to_num('I', 1). char_to_num(_, 0).

unroman(, 0).

unroman(Roman, X) :- string_length(Roman, Length), RestLen is Length - 1, NextLen is Length - 2, sub_string(Roman, 1, 1, RestLen, First), sub_string(Roman, 2, 1, NextLen, Next), sub_string(Roman, 2, RestLen, 0, Rest), char_to_num(First, FirstNum), char_to_num(Next, NextNum), FirstNum >= NextNum, unroman(Rest, RestNum), X is RestNum + FirstNum.

unroman(Roman, X) :- string_length(Roman, Length), RestLen is Length - 1, NextLen is Length - 2, sub_string(Roman, 1, 1, RestLen, First), sub_string(Roman, 2, 1, NextLen, Next), sub_string(Roman, 2, RestLen, 0, Rest), char_to_num(First, FirstNum), char_to_num(Next, NextNum), FirstNum < NextNum, unroman(Rest, RestNum), X is RestNum - FirstNum.</lang>

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

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</lang> Sample output:

1999
1666
25
954
2011

Python

<lang python>_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) )</lang>
Sample output
MCMXC 1990
MMVIII 2008
MDCLXVI 1666

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: <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+}
   expr [string map $map $rnum]0}

}</lang> Demonstrating: <lang tcl>foreach r {MCMXC MDCLXVI MMVIII} {

   puts "$r\t-> [fromRoman $r]"

}</lang> Output:

MCMXC	-> 1990
MDCLXVI	-> 1666
MMVIII	-> 2008

TUSCRIPT

<lang tuscript> $$ MODE TUSCRIPT LOOP roman_number="MCMXC'MMVIII'MDCLXVI" arab_number=DECODE (roman_number,ROMAN) PRINT "Roman number ",roman_number," equals ", arab_number ENDLOOP </lang> Output:

Roman number MCMXC equals 1990
Roman number MMVIII equals 2008
Roman number MDCLXVI equals 1666

Zsh

<lang 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

}</lang>