Task

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


 
:- module roman.
 
:- interface.
 
:- import_module io.
 
:- pred main(io::di, io::uo) is det.
 
:- implementation.
 
:- import_module char, int, list, string.
 
main(!IO) :-
    command_line_arguments(Args, !IO),
    filter(is_all_digits, Args, CleanArgs),
    foldl((pred(Arg::in, !.IO::di, !:IO::uo) is det :-
               ( Roman = to_roman(Arg) ->
                     format("%s => %s", [s(Arg), s(Roman)], !IO), nl(!IO)
               ;     format("%s cannot be converted.", [s(Arg)], !IO), nl(!IO) )
          ), CleanArgs, !IO).
 
:- func to_roman(string::in) = (string::out) is semidet.
to_roman(Number) = from_char_list(build_roman(reverse(to_char_list(Number)))).
 
:- func build_roman(list(char)) = list(char).
:- mode build_roman(in)         = out is semidet.
build_roman([]) = [].
build_roman([D|R]) = Roman :-
    map(promote, build_roman(R), Interim),
    Roman = Interim ++ digit_to_roman(D).
 
:- func digit_to_roman(char) = list(char).
:- mode digit_to_roman(in)   = out is semidet.
digit_to_roman('0') = [].
digit_to_roman('1') = ['I'].
digit_to_roman('2') = ['I','I'].
digit_to_roman('3') = ['I','I','I'].
digit_to_roman('4') = ['I','V'].
digit_to_roman('5') = ['V'].
digit_to_roman('6') = ['V','I'].
digit_to_roman('7') = ['V','I','I'].
digit_to_roman('8') = ['V','I','I','I'].
digit_to_roman('9') = ['I','X'].
 
:- pred promote(char::in, char::out) is semidet.
promote('I', 'X').
promote('V', 'L').
promote('X', 'C').
promote('L', 'D').
promote('C', 'M').
 
:- end_module roman.
 