Roman numerals/Encode: Difference between revisions
No edit summary |
|||
Line 43: | Line 43: | ||
XXV |
XXV |
||
CMXLIV |
CMXLIV |
||
=={{ALGOL 68|D}}== |
|||
[]CHAR roman = "MDCLXVmdclxvi"; |
|||
[]CHAR adjust roman = "CCXXmmccxxii"; |
|||
[]INT arabic = (1000000, 500000, 100000, 50000, 10000, 5000, 1000, 500, 100, 50, 10, 5, 1); |
|||
[]INT adjust arabic = (100000, 100000, 10000, 10000, 1000, 1000, 100, 100, 10, 10, 1, 1); |
|||
PROC arabic to roman = (INT mclxvi)STRING: ( |
|||
INT in := mclxvi; # 666 # |
|||
STRING out := ""; |
|||
FOR denominator TO UPB roman DO |
|||
INT div := in OVER arabic[denominator]; |
|||
in -:= div * arabic[denominator]; |
|||
out +:= div * roman[denominator]; |
|||
IF denominator <= UPB adjust roman THEN |
|||
IF in + adjust arabic[denominator] >= arabic[denominator] THEN |
|||
in +:= adjust arabic[denominator] - arabic[denominator]; |
|||
out +:= adjust roman[denominator] + roman[denominator] |
|||
FI |
|||
FI |
|||
OD; |
|||
out |
|||
); |
|||
main:( |
|||
[]INT test = (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,25,30,40,50,60,69,70, |
|||
80,90,99,100,200,300,400,500,600,666,700,800,900,1000,1009,1444,1666,1945,1997, 1999, |
|||
2000,2008,2500,3000,4000,4999,5000,6666,10000,50000,100000,500000,1000000,max int); |
|||
FOR key TO UPB test DO |
|||
INT val = test[key]; |
|||
printf(($g" - "gl$, val, arabic to roman(val))) |
|||
OD |
|||
) |
|||
=={{header|D}}== |
=={{header|D}}== |
||
Line 92: | Line 125: | ||
writefln("%20s - %s", x, toRoman(x)) ; |
writefln("%20s - %s", x, toRoman(x)) ; |
||
}</d> |
}</d> |
||
=={{header|Forth}}== |
=={{header|Forth}}== |
||
: vector create ( n -- ) 0 do , loop |
: vector create ( n -- ) 0 do , loop |
Revision as of 12:51, 2 June 2008
You are encouraged to solve this task according to the task description, using any language you may know.
Create a function taking a positive integer as its parameter and returning a string containing the Roman Numeral representation of that integer.
Modern Roman numerals are written by expressing each digit separately starting with the left most digit and skipping any digit with a value of zero. In Roman numerals 1990 is rendered: 1000=M, 900=CM, 90=XC; resulting in MCMXC. 2008 is written as 2000=MM, 8=VIII; or MMVIII. 1666 uses each Roman symbol in descending order: MDCLXVI.
Ada
<Ada> with Ada.Text_IO; use Ada.Text_IO;
procedure Roman_Numeral_Test is
function To_Roman (Number : Positive) return String is subtype Digit is Integer range 0..9; function Roman (Figure : Digit; I, V, X : Character) return String is begin case Figure is when 0 => return ""; when 1 => return "" & I; when 2 => return I & I; when 3 => return I & I & I; when 4 => return I & V; when 5 => return "" & V; when 6 => return V & I; when 7 => return V & I & I; when 8 => return V & I & I & I; when 9 => return I & X; end case; end Roman; begin return Roman (Number / 1000, 'M', 'M', 'M') & Roman (Number / 100 mod 10, 'C', 'D', 'M') & Roman (Number / 10 mod 10, 'X', 'L', 'C') & Roman (Number mod 10, 'I', 'V', 'X'); end To_Roman;
begin
Put_Line (To_Roman (1999)); Put_Line (To_Roman (25)); Put_Line (To_Roman (944));
end Roman_Numeral_Test; </Ada> Output:
MCMXCIX XXV CMXLIV
Template:ALGOL 68
[]CHAR roman = "MDCLXVmdclxvi"; []CHAR adjust roman = "CCXXmmccxxii"; []INT arabic = (1000000, 500000, 100000, 50000, 10000, 5000, 1000, 500, 100, 50, 10, 5, 1); []INT adjust arabic = (100000, 100000, 10000, 10000, 1000, 1000, 100, 100, 10, 10, 1, 1); PROC arabic to roman = (INT mclxvi)STRING: ( INT in := mclxvi; # 666 # STRING out := ""; FOR denominator TO UPB roman DO INT div := in OVER arabic[denominator]; in -:= div * arabic[denominator]; out +:= div * roman[denominator]; IF denominator <= UPB adjust roman THEN IF in + adjust arabic[denominator] >= arabic[denominator] THEN in +:= adjust arabic[denominator] - arabic[denominator]; out +:= adjust roman[denominator] + roman[denominator] FI FI OD; out ); main:( []INT test = (1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,25,30,40,50,60,69,70, 80,90,99,100,200,300,400,500,600,666,700,800,900,1000,1009,1444,1666,1945,1997, 1999, 2000,2008,2500,3000,4000,4999,5000,6666,10000,50000,100000,500000,1000000,max int); FOR key TO UPB test DO INT val = test[key]; printf(($g" - "gl$, val, arabic to roman(val))) OD )
D
This implementation in generally follows the rules implied by Modern Roman numerals, with some irregularity depend on whether numerals larger than M(1000) is used, eg. 4000 is converted to MV' if V' is used, MMMM if not. <d>module roman ; import std.stdio ;
const string[] Roman = ["V","X","L","C","D","M","I"] ; const int RLen = Roman.length - 1 ; const int[][] RDigit =
[[0],[0,0],[0,0,0],[0,1],[1],[1,0],[1,0,0],[1,0,0,0],[0,2],[0,0,0,0]] ;
const string[] Power = ["", "'","\"","`","~","^","#"] ; // arbitary _power_ symbols, or
// Power = ["1","2","3","4","5","6","7"] ; // for easier further processing
const int[][] Shift = [[0,0,0],[-1,0,0]] ;
string romanPart(int n, int part, bool extented) {
if (n == 0) return "" ; int[3] b ; b[1] = (2 * part) % RLen ; b[0] = part == 0 ? RLen : (RLen + b[1] - 1) % RLen ; b[2] = b[1] + 1 ; int power = part / 3 ; int[] shift = Shift[ b[1] == 0 && part != 0 ? 1 : 0] ; int[] Digit = !extented && n == 4 && part == 3 ? RDigit[$-1] : RDigit[n-1] ; string res ; foreach(inx ; Digit) res ~= Roman[b[inx]] ~ Power[power + shift[inx]] ; return res ;
} string toRoman(long n, bool extented = true) {
if(n < 0) throw new Exception("No negative Roman Numeral") ; if(n == 0) return "" ; if(!extented && n >= 5000) throw new Exception("Only smaller than 5000 allowed") ; string romans ; int part = 0 ; while (n > 0) { long m = n / 10 ; romans = romanPart(n - m*10, part, extented) ~ romans ; n = m ; part++ ; } return romans ;
} void main() {
auto test = [1L,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,25,30,40,50,60,69,70, 80,90,99,100,200,300,400,500,600,666,700,800,900,1000,1009,1444,1666,1945,1997, 1999, 2000,2008,2500,3000,4000,4999,5000,6666,10000,50000,100000,500000,1000000,long.max] ; foreach(x ; test) writefln("%20s - %s", x, toRoman(x)) ;
}</d>
Forth
: vector create ( n -- ) 0 do , loop does> ( n -- ) swap cells + @ execute ; \ these are all ( numerals -- ) :noname dup c@ emit 2 + c@ emit ; \ 9: IX :noname dup 1+ c@ emit c@ dup dup emit emit emit ; \ 8: VIII :noname dup 1+ c@ emit c@ dup emit emit ; \ 7: VII :noname dup 1+ c@ emit c@ emit ; \ 6: VI :noname 1+ c@ emit ; \ 5: V :noname dup c@ emit 1+ c@ emit ; \ 4: IV :noname c@ dup dup emit emit emit ; \ 3: III :noname c@ dup emit emit ; \ 2: II :noname c@ emit ; \ 1: I ' drop \ 0: no output 10 vector .digit : roman-rec ( numerals n -- ) 10 /mod dup if >r over 2 + r> recurse else drop then .digit ; : .roman ( n -- ) dup 0 4000 within 0= if ." EX LIMITO!" exit then s" IVXLCDM" drop swap roman-rec ;
Fortran
Fortran 90 and later
MODULE Roman IMPLICIT NONE CONTAINS FUNCTION numerals(number) CHARACTER(15) :: numerals CHARACTER(4) :: thousand_str, hundred_str, ten_str, unit_str INTEGER :: number, thousands, hundreds, tens thousands = number / 1000 SELECT CASE (thousands) CASE(0) thousand_str = "" CASE(1:4) thousand_str = REPEAT(STRING="M", NCOPIES=thousands) END SELECT number = MOD(number, 1000) hundreds = number / 100 SELECT CASE (hundreds) CASE(0) hundred_str = "" CASE(1:3) hundred_str = REPEAT(STRING="C", NCOPIES=hundreds) CASE(4) hundred_str = "CD" CASE(5) hundred_str = "D" CASE(6:8) hundred_str = "D"//REPEAT(STRING="C", NCOPIES=hundreds-5) CASE(9) hundred_str = "CM" END SELECT number = MOD(number, 100) tens = number / 10 SELECT CASE (tens) CASE(0) ten_str = "" CASE(1:3) ten_str = REPEAT(STRING="X", NCOPIES=tens) CASE(4) ten_str = "XL" CASE(5) ten_str = "L" CASE(6:8) ten_str = "L"//REPEAT(STRING="X", NCOPIES=tens-5) CASE(9) ten_str = "XC" END SELECT number = MOD(number, 10) SELECT CASE (number) CASE(0) unit_str = "" CASE(1:3) unit_str = REPEAT(STRING="I", NCOPIES=number) CASE(4) unit_str = "IV" CASE(5) unit_str = "V" CASE(6:8) unit_str = "V"//REPEAT(STRING="I", NCOPIES=number-5) CASE(9) unit_str = "IX" END SELECT numerals = TRIM(thousand_str)//TRIM(hundred_str)//TRIM(ten_str)//TRIM(unit_str) END FUNCTION END MODULE PROGRAM ROMAN_TEST USE Roman WRITE(*,*) numerals(2008) WRITE(*,*) numerals(1666) WRITE(*,*) numerals(3888) END PROGRAM ROMAN_TEST
Output
MMVIII MDCLXVI MMMDCCCLXXXVIII
Haskell
With an explicit decimal digit representation list:
digit x y z k = [[x],[x,x],[x,x,x],[x,y],[y],[y,x],[y,x,x],[y,x,x,x],[x,z]] !! (fromInteger k - 1) toRoman :: Integer -> String toRoman 0 = "" toRoman x | x < 0 = error "Negative roman numeral" toRoman x | x >= 1000 = 'M' : toRoman (x - 1000) toRoman x | x >= 100 = digit 'C' 'D' 'M' q ++ toRoman r where (q,r) = x `divMod` 100 toRoman x | x >= 10 = digit 'X' 'L' 'C' q ++ toRoman r where (q,r) = x `divMod` 10 toRoman x = digit 'I' 'V' 'X' x
Output:
*Main> map toRoman [1999,25,944] ["MCMXCIX","XXV","CMXLIV"]
J
rfd obtains Roman numerals from decimals, and dfr decimals from Roman numerals.
dfr=: 3 : 0 i=. 'IVXLCDM' i. y d=. i{1 5 10 50 100 500 1000 +/d*_1^i<}.i,_1 ) r100 =. <;._1 ' C CC CCC CD D DC DCC DCCC CM' r10 =. <;._1 ' X XX XXX XL L LX LXX LXXX XC' r1 =. <;._1 ' I II III IV V VI VII VIII IX' R1000=: , r100 ,&.>/ r10 ,&.>/ r1 rfd=: 3 : 0 ('M'$~<.y%1000),R1000{::~1000|y )
Copied, with permission, from the J Wiki. Examples of use will be found there.
Java
The helper function copies is added since Java does not support String multiplication. The conversion function returns null for a negative number, since Java does not have unsigned primitives. <java>public class RN{ public static void main(String args[]){ System.out.println(roman(1999)); System.out.println(roman(25)); System.out.println(roman(954)); } public static String roman(long n){ if(n < 1) return null; String result = ""; if(n >= 1000){ result+= (copies("M",(n / 1000))); n%= 1000; } if(n >= 900){ result+= "CM"; n%= 900; } if(n >= 500){ result+= "D"; n%= 500; } if(n >= 400){ result+= "CD"; n%= 400; } if(n >= 100){ result+= (copies("C",(n / 100))); n%= 100; } if(n >= 90){ result+= "XC"; n%= 90; } if(n >= 50){ result+= "L"; n%= 50; } if(n >= 40){ result+= "XL"; n%= 40; } if(n >= 10){ result+= (copies("X",(n / 10))); n%= 10; } if(n == 9){ result+= "IX"; n= 0; } if(n >= 5){ result+= "V"; n%= 5; } if(n == 4){ result+= "IV"; n= 0; } result+= (copies("I",n)); return result; }
public static String copies(String a, int n){ String result = ""; for(int i= 0;i < n;i++,result+= a); return result; } }</java> Output:
MCMXCIX XXV CMXLIV
Logo
make "patterns [[?] [? ?] [? ? ?] [? ?2] [?2] [?2 ?] [?2 ? ?] [?2 ? ? ?] [? ?3]] to digit :d :numerals if :d = 0 [output "||] output apply (sentence "\( "word (item :d :patterns) "\)) :numerals end to digits :n :numerals output word ifelse :n < 10 ["||] [digits int :n/10 bf bf :numerals] ~ digit modulo :n 10 :numerals end to roman :n if or :n < 0 :n >= 4000 [output [EX MODVS!]] output digits :n [I V X L C D M] end
print roman 1999 ; MCMXCIX print roman 25 ; XXV print roman 944 ; CMXLIV
OCaml
With an explicit decimal digit representation list:
<ocaml> let digit x y z = function
1 -> [x] | 2 -> [x;x] | 3 -> [x;x;x] | 4 -> [x;y] | 5 -> [y] | 6 -> [y;x] | 7 -> [y;x;x] | 8 -> [y;x;x;x] | 9 -> [x;z]
let rec to_roman x =
if x = 0 then [] else if x < 0 then invalid_arg "Negative roman numeral" else if x >= 1000 then 'M' :: to_roman (x - 1000) else if x >= 100 then digit 'C' 'D' 'M' (x / 100) @ to_roman (x mod 100) else if x >= 10 then digit 'X' 'L' 'C' (x / 10) @ to_roman (x mod 10) else digit 'I' 'V' 'X' x
</ocaml>
Output:
# to_roman 1999;; - : char list = ['M'; 'C'; 'M'; 'X'; 'C'; 'I'; 'X'] # to_roman 25;; - : char list = ['X'; 'X'; 'V'] # to_roman 944;; - : char list = ['C'; 'M'; 'X'; 'L'; 'I'; 'V']
Perl
Perligata outputs numbers in Arabic, but the verb come ("beautify") may be used to convert numbers to proper Roman numerals:
per quisque in I tum C conscribementum sic hoc tum duos multiplicamentum comementum egresso scribe. cis
Ruby
Roman numeral generation was used as an example for demonstrating Test Driven Development in Ruby.