views:

2211

answers:

29

Write a program that take a single command line argument N and prints out the corresponding Roman Numeral.

Eg N = 2009 should print MMIX.

Let's say this should work for 0 < N < 3000.

(Had fun playing my first ever round of code golf with the Christmas edition, and thought this could fit for New Year. Googled to see if this has come up before elsewhere and it looks like it hasn't, but let me know if this is too hard or too easy or if the rules need changing. )

Happy MMIX!

A: 

Real simple: pass the query to Google and screenscrape the answer. Next. :p

BTW, shouldn't this be a community wiki?

AnonJr
Yes - have changed to community mode. If you can write said screenscraper (and keep it short obviously) then go for it!
It's not so simple. Google rejects queries that don't appear to come from a standard browser. Moreover, the resulting HTML is a mess. You won't be happy with regular expressions.
Federico Ramponi
google_roman() { lynx -dump http://google.com/search?q=$1+in+roman \ |awk "/^$1 = /{print \$3}"; }; google_roman 4999
system PAUSE
+14  A: 

In C#, as an extension method to Int32:

public static class Int32Extension {
    public static string ToRomanNumeral(this int number) {
        Dictionary<int, string> lookup = new Dictionary<int, string>() {
            { 1000000, "M_" },
            { 900000, "C_D_" },
            { 500000, "D_" },
            { 400000, "C_D_" },
            { 100000, "C_" },
            { 90000, "X_C_" },
            { 50000, "L_" },
            { 40000, "X_L_" },
            { 10000, "X_" },
            { 9000, "MX_"},
            { 5000, "V_" },
            { 4000, "MV_" },
            { 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" }
        };

        StringBuilder answer = new StringBuilder();
        foreach (int key in lookup.Keys.OrderBy(k => -1 * k)) {
            while (number >= key) {
                number -= key;
                answer.Append(lookup[key]);
            }
        }
        return answer.ToString();
    }
}

The underscores should be overlines above the respective letter to be true Roman Numeral.

Jason
+1 for being the only answer which considers the higher-order roman symbols.
Kent Fredric
This code works, but there is no guarantee it will in .NET 4.0, since the order in which Dictionary produces its keys is undefined. It just so happens to work in this example. I would change the code to use a List instead, to guarantee the outcome.
Lasse V. Karlsen
@Lasse V. Karlsen: Addressed.
Jason
For code golf, are you allowed to save characters by using 'var'?
Roger Lipscombe
+1  A: 

From a vaguely C-like language called LPC (precursor of Pike):

string roman_numeral(int val) {
    check_argument(1, val, #'intp);
    unless(val)
        return "N";
    string out = "";
    if(val < 0) {
        out += "-";
        val = -val;
    }
    if(val >= 1000) {
        out += "M" * (val / 1000);
        val %= 1000;
    }
    if(val >= 100) {
        int part = val / 100;
        switch(part) {
        case 9      :
            out += "CM";
            break;
        case 6 .. 8 :
            out += "D" + ("C" * (part - 5));
            break;
        case 5      :
            out += "D";
            break;
        case 4      :
            out += "CD";
            break;
        default     :
            out += "C" * part;
            break;
        }
        val %= 100;
    }
    if(val >= 10) {
        int part = val / 10;
        switch(part) {
        case 9      :
            out += "XC";
            break;
        case 6 .. 8 :
            out += "L" + ("X" * (part - 5));
            break;
        case 5      :
            out += "L";
            break;
        case 4      :
            out += "XL";
            break;
        default     :
            out += "X" * part;
            break;
        }
        val %= 10;
    }
    switch(val) {
    case 9      :
        out += "IX";
        break;
    case 6 .. 8 :
        out += "V" + ("I" * (val - 5));
        break;
    case 5      :
        out += "V";
        break;
    case 4      :
        out += "IV";
        break;
    default     :
        out += "I" * val;
        break;
    }
    return out;
}
chaos
And for Pike: http://stackoverflow.com/questions/394574/#394734
some
That language taught me OOP back in 1992.
Jonas Elfström
+11  A: 

Common lisp (SBCL). 63 characters counted by "wc -c".

(format t "~@R~%" (parse-integer (elt *posix-argv* 1)))
(quit)

This only works for numbers upto 3999.

using a build-in in is cheating ;-)
mfx
Nobody ever says that about Perl regular expressions.If it's trivial for a language to do something, you should do it. That's part of what makes coding it an art.
Indeed, using Perl, it suffices to use 62 characters. See my post below.
A. Rex
+2  A: 

In Python - taken from ActiveState (credits: Paul Winkler) and compressed a bit:

def int2roman(n):
   if not 0 < n < 4000: raise ValueError
   ints = (1000, 900,  500, 400, 100,  90, 50,  40, 10,  9,   5,  4,   1)
   nums = ('M',  'CM', 'D', 'CD','C', 'XC','L','XL','X','IX','V','IV','I')
   result = ""
   for i in range(len(ints)):
      count = int(n / ints[i])
      result += nums[i] * count
      n -= ints[i] * count
   return result
Federico Ramponi
Nice - compressed down I get 293 bytes, including command-line arg reading. The implicit floor in integer division really saves some characters!
Alabaster Codify
+5  A: 

Perl, 19 strokes. Guaranteed to work for values between 1 and 12.

sub r{chr 8543+pop}
Chris Jester-Young
well it's short, but has little capabilities...
Gnark
+1  A: 

Python, 190 bytes. Based on snippet from ActiveState, via Federico.

A few small optimisations: removal of superfluous int() call, splitting string to get array, remove whitespace, ...

import sys
n=int(sys.argv[1])
N=(1000,900,500,400,100,90,50,40,10,9,5,4,1)
r=""
for i in range(len(N)):
 c=n/N[i]
 r+='M,CM,D,CD,C,XC,L,XL,X,IX,V,IV,I'.split(',')[i]*c
 n-=N[i]*c
print r

EDIT: superfluous, not spurious, and remove range check - thanks to Chris and dreeves! Stole idea of using symbol array inline from balabaster.

Alabaster Codify
s/spurious/superfluous/
dreeves
You can throw out the range check too, and just say that the program works only for 1..3999, which is what my Perl one says, and the Common Lisp submission. :-P
Chris Jester-Young
You can remove the parens from N's assignment. You can also move the for onto a single line, separating the statements by semicolon. That will save you another 4 bytes.
recursive
+6  A: 

C#: 179 chars (not including spaces/tabs)

    static string c(int a)
    {
        int[] v = { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 };
        var s = ""; 
        for ( var i = 0; i < 13; i++ )
            while (a >= v[i])
            {
                a -= v[i];
                s += "M CM D CD C XC L XL X IX V IV I".Split()[i];
            }
        return s;
    }
BenAlabaster
I think if you delimited your numerals with a space instead of a pipe you could just call `Split()` without an argument.
Phil
@Phil Thanks, I learned something new today :) I never knew that worked. It makes me wonder how many times I've used split with needless params in the past.
BenAlabaster
+1  A: 

VB: 193 chars

Function c(ByVal a)
    Dim v() = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}
    Dim s = ""
    For i = 0 To 12
        While a >= v(i)
            a -= v(i)
            s += "M|CM|D|CD|C|XC|L|XL|X|IX|V|IV|I".Split("|")(i)
        End While
    Next
    Return s
End Function
BenAlabaster
The newlines are significant in VB so you have to count them
erikkallen
A: 

Java: 286 significant characters

public class R {

    String[]x="M,CM,D,C,XC,L,X,IX,V,I".split(",");
    int[]n={1000,900,500,100,90,50,10,9,5,1};

    String p(String s,int d,int i){return 10<=i?s:n[i]<=d?p(s+x[i],d-n[i],i):p(s,d,i+1);}

    public static void main(String[] a) {
        System.out.println(new R().p("",Integer.parseInt(a[0]),0));
    }

}

By "significant characters", I mean the printing characters and required spaces (e.g. between type and argument), but not pure cosmetic whitespace (newlines and indentation).

joel.neely
+2  A: 

Perl, 145 strokes (if you strip out all the newlines, which are optional), valid for 1..3999:

%t=qw(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);
$d=pop;
for(sort{$b<=>$a}keys%t){$r.=$t{$_}x($d/$_);$d%=$_}
print$r

Some would say I could use say, but I don't have a say-capable Perl version here. Feel free to subtract 2 off the stroke count if using say works. :-)

For non-Perl programmers, this program exploits a number of useful Perl features:

  1. Hashes are constructed from lists of even length.
  2. Lists of strings can be specified in a compact syntax, using qw.
  3. Strings can auto-coerce into integers, as used in the <=> comparison operator in sorting the keys.
  4. There is an x operator which makes copies of strings/lists. Unfortunately for golfing here, x has identical precedence to /; if / were higher, the brackets would have been optional too.
Chris Jester-Young
You can squeeze out a few characters by doing the sorting yourself. (Bonus teaching: hash slice assignment) Also, stmt mod 'for' saves 2 chars. @d=(1000,900,500,400,100,90,50,40,10,9,5,4,1);@r{@d}=qw(M CM D CD C XC L CL X IX V IV I);$d=pop;$r.=$r{$_}x($d/$_),$d%=$_ for@d;print$r
JB
A: 

Delphi (or Pascal, there's nothing Delphi-specific here):

Function ToRoman(N : Integer) : String;

Const
    V : Array [1..13] of Word = (1000,900,500,400,100,90,50,40,10.9,5,4,1);
    T : Array [1..13] of String = ('M','CM','D','CD','C','XC','L','XL','X','IX','V','I');

Var I : Word;

Begin
    I := 1;
    Repeat
        While N < V[I] do Inc(I);
        Result := Result + T[I];
        N := N - V[I];
    Until N = 0;
End;

How is everyone getting the character counts? (I count 8 essential spaces, all the rest are simply for formatting.)

Loren Pechtel
Some text editors, like vi, tell you how many bytes are in the file. Or, you can use wc. No comments about what tools are available in non-Unix platforms though. :-P
Chris Jester-Young
copy and paste into MS word?
chakrit
+3  A: 

Perl 5.10

perl -nE'@l=qw{1 I 4 IV 5 V 9 IX 10 X 40 XL 50 L 90 XC 100 C 400 CD 500 D 900 CM 1000 M};
$o="";while(@l){$o.=pop(@l)x($_/($c=pop @l));$_%=$c;}say$o'

You input a line, it gives you the Roman numeral equivelent. This first version even lets you input more than one line.

Here is a shorter version that only works for one line, and ignores edge cases. so 4 becomes IIII instead of IV.

perl -nE'@l=qw{1 I 5 V 10 X 50 L 100 C 500 D 1000 M};
while(@l){$o.=pop(@l)x($_/($c=pop @l));$_%=$c;}say$o'

Here is what the first version would look like as a Perl script.

use 5.010;
while(<>){
  @l=qw{1 I 4 IV 5 V 9 IX 10 X 40 XL 50 L 90 XC 100 C 400 CD 500 D 900 CM 1000 M};
  $o="";
  while(@l){
    $o .= pop(@l) x ($_/($c=pop @l));
    # $l = pop @l;
    # $c = pop @l;
    # $o .= $l x ($_/$c);

    $_ %= $c;
  }
  say $o;
}
Brad Gilbert
Does Perl actually guarantee left-to-right execution order? I know it "works in this implementation", but is it actually guaranteed (e.g., Java guarantees it)? Curious. For example, in C, the right-hand pop is just as likely as the left-hand one to be executed first.
Chris Jester-Young
For infix operators, like `x`, the left side is evaluated before the right side.
Brad Gilbert
I was going to post a Unicode version that would work for any positive integer, unfortunately it seems to be extremely difficult.
Brad Gilbert
A: 

Here is a C solution in 252 meaningful chars. Valid from 0 <= i < 4000. Mostly I wrote this because so many solutions include IV and IX at array points. Decoding it: t is our temp buffer that we back fill so that we don't have to reverse it on output. The buffer passed in must be at least 16 chars (for 3888 -> MMMDCCCLXXXVIII).

 char* i2r(int i, char* r) {
     char t[20];
     char* o=t+19;*o=0;
     char* s="IVXLCDMM";
     for (char*p=s+1;*p&&i;p+=2) {
         int x=i%10;
         if (x==9) {*--o=p[1];*--o=p[-1];}
         else if (x==4) {*--o=*p;*--o=p[-1];}
         else {
             for(;x&&x!=5;--x)*--o=p[-1];
             if(x)*--o=*p;
         }
         i/=10;
     }
     return strcpy(r,o);
 }

And I always forget to put the main on. So much for 252 chars:

 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 void main(int a,char**v){
     char buf[16];
     printf("%s\n",i2r(atoi(v[1])));
 }
jmucchiello
strager
+3  A: 

Pike

60 characters, valid for 0 to 10000:

int main (int c, array a) {
    write(String.int2roman((int)a[1]));
}
some
Cheating much? :P
Bob Somers
nope, just using whats available ;)
some
just *feels* like cheating :)
annakata
A: 

Language: C, Char count: 195

Based heavily off of me.yahoo.com/joe_mucchielle's C solution:

char t[99],*o=t+99,*s="IVXLCDMM",*p,x;n(v){*--o=p[v];}main(int i,int**v){i=atoi(v[1]);*o=0;
for(p=s+1;*p&&i;p+=2){x=i%10;if(x%5==4)n(x==9),n(-1);else{for(;x%5;--x)n(-1);if(x)n(0);}i/=10;}puts(o);}
strager
+4  A: 

Python, 173 bytes.

r=lambda n:o[n]if n<10 else''.join(dict(zip('ivxlc','xlcdm'))[c]for c in r(n//10))+o[n%10]
o=' i ii iii iv v vi vii viii ix'.split(' ')
import sys
print r(int(sys.argv[1]))

(I first saw this algorithm in Gimpel's Algorithms in Snobol4; Snobol expressed it more elegantly.)

Darius Bacon
This is great! Recursive application on n//10 is a much more terse and effective usage of the independence between digits than simply popping one off at a time, most significant first.
Alabaster Codify
A: 

Language: Erlang, Char count: 222

EDIT2: Erlang preprocessor allows some sort of unbalanced macros so this version is 9 chars shorter.

-module(n2).
-export([y/1]).
-define(D(V,S),n(N)when N>=V->[??S|n(N-V)];).
y(N)->io:format(n(N)).
?D(1000,M)?D(900,CM)?D(500,D)?D(400,CD)?D(100,C)?D(90,XC)?D(50,L)?D(40,XL)?D(10,X)?D(9,IX)?D(5,V)?D(4,IV)?D(1,I)n(0)->[10].

EDIT: Shorter version inspired by Darius version (231 chars)

-module(n).
-export([y/1]).
y(N)->io:format([n(N),10]).
n(N)when N>9->[Y||C<-n(N div 10),{K,Y}<-lists:zip("IVXLC","XLCDM"),K==C]++o(N rem 10);n(N)->o(N).
o(N)->lists:nth(N+1,[[]|string:tokens("I II III IV V VI VII VIII IX"," ")]).

It's less readable but save 2 chars (233 chars).

-module(n).
-export([y/1]).
-define(D(V,S),n(N)when N>=V->[??S|n(N-V)]).
y(N)->io:format(n(N)).
?D(1000,M);?D(900,CM);?D(500,D);?D(400,CD);?D(100,C);?D(90,XC);?D(50,L);?D(40,XL);?D(10,X);?D(9,IX);?D(5,V);?D(4,IV);?D(1,I);n(0)->[10].

Command line version:

-module(n).
-export([y/1]).
-define(D(V,S),n(N)when N>=V->[??S|n(N-V)]).
y([N])->io:format(n(list_to_integer(N))),init:stop().
?D(1000,M);?D(900,CM);?D(500,D);?D(400,CD);?D(100,C);?D(90,XC);?D(50,L);?D(40,XL);?D(10,X);?D(9,IX);?D(5,V);?D(4,IV);?D(1,I);n(0)->[10].

Invocation:

$ erl -noshell -noinput -run n y 2009
MMIX

EDIT: I saved 17 chars using literal macro expansion.

Hynek -Pichi- Vychodil
+2  A: 

Language: dc (through shell) Char count:122

EDIT: q is equivalent of 2Q

dc -e '[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[szsz2Q]sq[~Sa[d0!<qrdPr1-lbx]dsbxLarz3<c]dscx10P' <<<$1

EDIT: two more chars by optimizing main loop stack manipulations

dc -e '[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[szsz2Q]sq[~Sa[d0!<qrdPr1-lbx]dsbxLarz3<c]dscx10P' <<<$1

EDIT: save 2 chars

dc -e '[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?[sz2Q]sq[r~r[d0!<qSardPrLa1-lbx]dsbxrszz2<c]dscx10P' <<<$1

Previous version:

dc -e '[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?[sz2Q]sq[r~r[d0!<qSaSadPLaLa1-lbx]dsbxrszz2<c]dscx10P' <<<$1
Hynek -Pichi- Vychodil
+5  A: 

Language: JavaScript.

129 chars without the added formatting

The following code is a result of coding quiz which which took place at pl.comp.lang.javascript newsgrup several years ago. I'm not the author of the code.

function rome(N,s,b,a,o){
  for(s=b='',a=5;N;b++,a^=7)for(o=N%a,N=N/a^0;o--;)
  s='IVXLCDM'.charAt(o>2?b+N-(N&=~1)+(o=1):b)+s;return s
}

Original post by Elus

Elus
+4  A: 

Language: C, Char count: 174

#define R(s,v)for(;n>=v;n-=v)printf(#s);
main(int n,char**a){n=atoi(a[1]);R(M,1000)R(CM,900)R(D,500)R(CD,400)R(C,100)R(XC,90)R(L,50)R(XL,40)R(X,10)R(IX,9)R(V,5)R(IV,4)R(I,1)}
Boojum
Could you not further reduce it by making R a function (like I did for 'n' in my answer)?
strager
Ah, nevermind -- I see your trick with string'ing using the macro. Crafty. =]
strager
+1  A: 

Ruby, 136 chars

n = $*[0].to_i
for k,v in [1e3,900,500,400,100,90,50,40,10,9,5,4,1].zip %w{M CM D CD C XC L XL X IX V IV I}
  until n < k
    n -= k
    print v
  end
end
Jules
Nice! You could shorten the inner loop with this stupid trick `[$><<v, n-=k] until n<k`.
Jonas Elfström
A: 

Railo CFML - 53 chars, 46 without whitespace...

<cfoutput>
    #NumberFormat( N , 'roman' )#
</cfoutput>


Or, for other CF engines, not sure if these are shortest, but they'll do for now...


CFML - 350..453 characters:

<cffunction name="RomanNumberFormat">
    <cfset var D = ListToArray('M,CM,D,C,XC,L,X,IX,V,IV,I') />
    <cfset var I = [1000,900,500,100,90,50,10,9,5,4,1] />
    <cfset var R = '' />
    <cfset var x = 1 />
    <cfset var A = Arguments[1] />
    <cfloop condition="A GT 0">
     <cfloop condition="A GTE I[x]">
      <cfset R &= D[x] />
      <cfset A -= I[x] />
     </cfloop>
     <cfset x++ />
    </cfloop>
    <cfreturn R />
</cffunction>

<cfoutput>
    #RomanNumberFormat(N)#
</cfoutput>


CFScript - 219..323 characters:

<cfscript>
    function RomanNumberFormat(A)
    {
     var D = ListToArray('M,CM,D,C,XC,L,X,IX,V,IV,I');
     var I = [1000,900,500,100,90,50,10,9,5,4,1];
     var R = '';
     var x = 1;
     while ( A > 0 )
     {
      while( A >= I[x] )
      {
       R &= D[x];
       A -= I[x];
      }
      x++;
     }
     return R;
    }

    WriteOutput( RomanNumberFormat(N) );
</cfscript>
Peter Boughton
+14  A: 

Perl: 69 strokes (count 'em!)

Sixty-nine strokes including calling perl in the first place:

$ perl -ple's!.!($#.=5x$&*8%29628)=~y$IVCXL4620-8$XLMCDIXV$d!eg;last}{'
3484
MMMCDLXXXIV
  • Reads a single line, writes a single line.
  • Works from 0 to 3999, inclusive. (Prints empty string for 0.)
  • In Perl golf competitions, this is usually scored as 62 strokes = 58 for the code + 4 for the switches.
  • Why, yes, those are mismatched braces. Thanks for asking. =)

Credits: originally due to Ton Hospel. The trick involving the mismatched braces is from rev.pl in this post (which incidentally, is ingenious).

A. Rex
You should remove ;last}{ and save 7 chars. Furthermore i think you have not to take into account the invocation of perl interpreter, so you save 5 chars more. Count is then 57 chars !
M42
+2  A: 

I'm no Haskell expert, and this is too long to be a winner, but here's a solution I wrote a while back to solve Euler #89.

toRoman 0 = ""
toRoman 1 = "I"
toRoman 2 = "II"
toRoman 3 = "III"
toRoman 4 = "IV"
toRoman n
| n >= 1000 = repeatRoman 'M' 1000
| n >= 900 = subtractRoman "CM" 900
| n >= 500 = subtractRoman "D" 500
| n >= 400 = subtractRoman "CD" 400
| n >= 100 = repeatRoman 'C' 100
| n >= 90 = subtractRoman "XC" 90
| n >= 50 = subtractRoman "L" 50
| n >= 40 = subtractRoman "XL" 40
| n >= 10 = repeatRoman 'X' 10
| n >= 9 = subtractRoman "IX" 9
| n >= 5 = subtractRoman "V" 5
| otherwise = error "Hunh?"
where
 repeatRoman c n' = (take (n `div` n') (repeat c)) ++ (toRoman $ n `mod` n')
 subtractRoman s n' = s ++ (toRoman $ n - n')
Gregory Higley
A: 

In C# (running on .NET 4 RC), with 335 chars (if you remove the extraneous formatting).

using System;
using System.Linq;
class C
{
    static void Main()
    {
        Console.WriteLine(
                Console.ReadLine()
                .PadLeft(4,'0')
                .Select(d=>d-'0')
                .Zip(new[]{"  M","MDC","CLX","XVI"},(x,y)=>new{x,y})
                .Aggregate("",(r,t)=>r+
                                    new string(t.y[2],t.x%5/4)+
                                    new string(t.y[0],t.x%5/4*t.x/5)+
                                    new string(t.y[1],Math.Abs(t.x%5/4-t.x/5))+
                                    new string(t.y[2],t.x%5%4)));
    }
}

I know it does not beat the current best C# answer (182 chars) but this is just one big LINQ one-liner. Once I saw a raytracer written as a single LINQ query, I started approaching code golfs from this perspective.

Since this approach is functional, I'm working on a Haskell version of the same algorithm (will surely be shorter).

Martinho Fernandes
A: 

Haskell version of my C#/LINQ answer, 234 chars:

main=putStrLn.foldr(\(y,x)s->concat[s,r(a x)(y!!2),r(a x*div x 5)(y!!0),r(abs(a x-div x 5))(y!!1),r(mod(mod x 5)4)(y!!2)])"".zip["XVI","CLX","MDC","  M"].map(read.(:[])).take 4.(++"000").reverse=<<getLine
r=replicate
a x=div(mod x 5)4
Martinho Fernandes
+1  A: 

J, 20 characters!

'MDCLXVI'#~(7$5 2)#:

Usage:

   'MDCLXVI'#~(7$5 2)#: 2009
MMVIIII

Okay, it doesn't do subtraction properly, but hey it's pretty cool!

Explanation;

(7$5 2)

This takes the right argument (the list 5 2) and turns it into a list of size 7 - namely 5 2 5 2 5 2 5.

(7$5 2)#: 2009

This does the "anti-base" operation - basically doing iterative div and mod operations, returning the list 2 0 0 0 0 0 1 4.

Then #~ uses the previous list as a tally to pull corresponding characters out of 'MDCLXVI'.

David
+2  A: 

A simple Haskell version, that still keeps clarity. 205 characters, including white space.

l = ["M","CM","L","CD","C","XC","L","XL","X","IX","V","IV","I"]
v = [1000,900,500,400,100,90,50,40,10,9,5,4,1]
roman n i
    | n == 0 = ""
    | n >= v!!i = l!!i ++ roman (n-v!!i) i
    | otherwise = roman n (i+1)
Thomas Ahle