Roman numerals/Decode: Difference between revisions
(→{{header|Perl 6}}: make case insensitive, and include both forms in output) |
(Added K Version) |
||
Line 288: | Line 288: | ||
2008 |
2008 |
||
1666</pre> |
1666</pre> |
||
=={{header|K}}== |
|||
{{trans|J}} |
|||
<lang k> romd: {v:1 5 10 50 100 500 1000@"IVXLCDM"?/:x; +/v*_-1^(>':v),0}</lang> |
|||
'''Example:''' |
|||
<lang k> romd'("MCMXC";"MMVIII";"MDCLXVI") |
|||
1990 2008 1666</lang> |
|||
=={{header|Lua}}== |
=={{header|Lua}}== |
||
<lang lua>function ToNumeral( roman ) |
<lang lua>function ToNumeral( roman ) |
||
Line 317: | Line 327: | ||
<pre>1990 |
<pre>1990 |
||
2008 |
2008 |
||
1666</pre> |
1666</pre>romd'("MCMXC";"MMVIII";"MDCLXVI") |
||
=={{header|PARI/GP}}== |
=={{header|PARI/GP}}== |
Revision as of 15:48, 21 June 2011
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.
C
Note: the code deliberately did not distinguish between "I", "J" or "U", "V", doing what Romans did for fun. <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 };
/* 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(char * roman) {
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() {
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;
} </lang>
C++
<lang C++>#include <boost/array.hpp>
- include <iostream>
- include <string>
- include <algorithm>
- include <functional>
class MySearch : public std::unary_function<std::pair<std::string , int> , bool> { private : std::string value ; public :
MySearch( const std::string & word ) : value( word ) { } ; bool operator( )( const std::pair<std::string , int> &p ) const { return p.first == value ; }
} ;
int RomanToInt( const std::string &roman ) {
typedef boost::array<std::pair<std::string , int> , 7> RomanType ; typedef boost::array<std::pair<std::string , int> ,7>::const_iterator PSI ; static RomanType Romans = { std::make_pair( "I" , 1 ) , std::make_pair( "V" , 5 ) , std::make_pair( "X" , 10 ) , std::make_pair( "L" , 50 ) , std::make_pair( "C" , 100 ) , std::make_pair( "D" , 500 ) , 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( ) {
std::cout << "MCMXC in Roman numerals is " << RomanToInt( "MCMXC" ) << " in Arabic!\n" ; std::cout << "And MDCLXVI for the Romans is " << RomanToInt( "MDCLXVI" ) << " in better known Arabic figures!\n" ; return 0 ;
}</lang> Output:
MCMXC in Roman numerals is 1990 in Arabic! And MDCLXVI for the Romans is 1666 in better known Arabic figures!
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> Alternative version: <lang d>import std.regex, std.algorithm;
int[string] w2s; 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];
}
int toArabic(string s) {
auto ms = match(s, "CM|CD|XC|XL|IX|IV|[MDCLXVI]"); return reduce!q{ a + b }(map!((m){ return w2s[m.hit]; })(ms));
}
void main() {
assert(toArabic("MCMXC") == 1990); assert(toArabic("MMVIII") == 2008); assert(toArabic("MDCLXVI") == 1666);
}</lang>
Fortran
<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>
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
K
<lang k> romd: {v:1 5 10 50 100 500 1000@"IVXLCDM"?/:x; +/v*_-1^(>':v),0}</lang>
Example:
<lang k> romd'("MCMXC";"MMVIII";"MDCLXVI") 1990 2008 1666</lang>
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
romd'("MCMXC";"MMVIII";"MDCLXVI")
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>
Perl
<lang Perl>use 5.10.0;
sub from_roman {
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, );
my ($r, $n) = @_; while ($r =~ /(M|CM|D|CD|C|XC|L|XL|X|IX|V|IV|I)/ig) { $n += $trans{uc $1} } return $n
}
say "$_: ", from_roman($_) for qw(MCMXC MDCLXVI MMVIII);</lang> Output:<lang>MCMXC: 1990 MDCLXVI: 1666 MMVIII: 2008</lang>
Perl 6
<lang perl6>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>;</lang> Output:
MCMXC => 1990 MDCLXVI => 1666 MMVIII => 2008
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
<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>