Convert arbitrary roman numeral input to integer output

11

The usual rules: no external resources, or the like. Input will be a positive integer in valid Roman numeral format less than 10 thousand, say.

Use all the Roman numeral characters:

  • I = 1
  • V = 5
  • X = 10
  • L = 50
  • C = 100
  • D = 500
  • M = 1,000

Respect subtractive notation (copied from the Wikipedia page):

  • the numeral I can be placed before V and X to make 4 units (IV) and 9 units (IX) respectively
  • X can be placed before L and C to make 40 (XL) and 90 (XC) respectively
  • C can be placed before D and M to make 400 (CD) and 900 (CM) according to the same pattern[5]

Ben Reich

Posted 2013-12-27T15:20:16.113

Reputation: 1 577

Question was closed 2013-12-28T09:38:12.547

How should we represent ten thousand? MMMMMMMMMM or something else? – ProgramFOX – 2013-12-27T15:34:50.693

@ProgramFOX That sounds right. I've changed the spec to under 10,000. – Ben Reich – 2013-12-27T15:37:37.873

Answers

8

Mathematica, 33 chars

FromDigits[InputString[],"Roman"]

alephalpha

Posted 2013-12-27T15:20:16.113

Reputation: 23 988

I'd say "FromDigits" violates "no external resources, or the like". – Sverre Rabbelier – 2013-12-28T01:53:20.677

6

JavaScript, 98 91 characters:

for(k=d=l=0,a=prompt();i={I:1,V:5,X:10,L:50,C:100,D:500,M:1e3}[a[k++]];l=i)d+=i>l?i-2*l:i;d

Expanded form:

a = prompt(), // a is input string
d = 0,        // d is result sum
l = 0,        // l is last value processed (for comparison with current val)
k = 0,        // k is current position in string

v = {I:1,V:5,X:10,L:50,C:100,D:500,M:1e3}; // map of values

// for each char in string a[k] (left to right), put its base-10 value in i
while(i = v[a[k++]]) {

    // if previous left-side value is lower than this value,
    //     it should have been substracted, so subject twice its value
    //     (once to negate the past addition, and again to apply negative value)
    // regardless, add this value
    if(i>l) {
        d += i - 2*l;
    } else {
        d += i;
    }

    // store this value as previous value
    l=i;
}

// yield result
d;

apsillers

Posted 2013-12-27T15:20:16.113

Reputation: 3 632

You can save some characters by removing l=i and replacing d+=i>l?i-2*l:i with d+=i>l?i-2*l:l=i – ProgramFOX – 2013-12-27T16:37:33.443

@ProgramFOX This doesn't quite seem to work, because l doesn't get reassigned in the true case. (For example: IX is rendered as 11.) This may have worked in my previous right-to-left code, but I think this doesn't apply anymore. – apsillers – 2013-12-27T16:50:53.657

Oh yes, that's true. You also need to replace your > condition with >= if you want to remove the l=i condition. I don't know whether it still saves characters then, though. – ProgramFOX – 2013-12-27T16:52:16.333

3

Scala 195

val t=Map('I'->1,'V'->5,'X'->10,'L'->50,'C'->100,'D'->500,'M'->1000)
def f(n:String)={val r=n.substring(1).foldLeft((0,t(n.head)))((a,x)=>(if(a._2>=t(x))a._1+a._2 else a._1-a._2,t(x)));r._1+r._2}

Dan G

Posted 2013-12-27T15:20:16.113

Reputation: 191

3

GolfScript: 83

0\{[77 68 67 76 88 86 73]?[1000 500 100 50 10 5 1]=}%{)@.@.@<{~)}{}if@.}do;{+\.}do;

Ben Reich

Posted 2013-12-27T15:20:16.113

Reputation: 1 577

If only we could make the mapping shorter... – Dan G – 2013-12-27T21:57:38.427

Props to @DanG for the help – Ben Reich – 2013-12-27T21:57:52.117

3

MS Excel,12

Enter a value in A1, then enter this in A2

=ARABIC(A1)

scrblnrd3

Posted 2013-12-27T15:20:16.113

Reputation: 1 554

If you change the language to Google Sheets you can drop the terminal ) for -1 Byte – Taylor Scott – 2017-11-05T15:07:04.243

2

Python 201 characters

m={'IV':4,'IX':9,'XL':40,'XC':90,'CD':400,'CM':900,'I':1,'V':5,'X':10,'L':50,'C':100,'D':500,'M':1000}
f=lambda s:m[s] if s in m else(m[s[:2]]+f(s[2:])if s[:2]in m else m[s[0]]+f(s[1:]))
f(raw_input())

Example:

>>> m={'IV':4,'IX':9,'XL':40,'XC':90,'CD':400,'CM':900,'I':1,'V':5,'X':10,'L':50,'C':100,'D':500,'M':1000};f=lambda s:m[s] if s in m else(m[s[:2]]+f(s[2:])if s[:2]in m else m[s[0]]+f(s[1:]));f(raw_input())
MMMCMLXXXVII
3987

119 117 characters

>>> X=[dict(zip('MDCLXVI',(1e3,500,100,50,10,5,1)))[x]for x in raw_input()];sum((x,-x)[x<y]for x,y in zip(X,X[1:]))+X[-1]
MMMCMLXXXVII
3987.0

Expanded form:

>>> # Maps roman numbers to their decimal counterpart
>>> roman_map = dict(zip('MDCLXVI',(1e3,500,100,50,10,5,1)))
>>> # Maps the list of input number to the corresponding decimals
>>> X=[roman_map[x]for x in raw_input()]
>>> # A number will be added (x) or subtracted (-x) depending on its value vs the next number
>>> # For instance CM -> [100, 1000], 100 < 1000, therefore -100, +1000
>>> result = sum((x,-x)[x<y] for x,y in zip(X,X[1:])) # True = 1, False = 0
>>> # Adding the last element (which is always added)
>>> result += X[-1]

Valentin CLEMENT

Posted 2013-12-27T15:20:16.113

Reputation: 321

I dig the 1e3 one-char savings but the question demands "integer output". – Darren Stone – 2013-12-27T19:01:57.530

1

JavaScript, 198 characters

n={I:1,V:5,X:10,L:50,C:100,D:500,M:1e3,IV:4,IX:9,XL:40,XC:90,CD:400,CM:900},s=prompt(),a=i=0;for(;i<s.length;i++){b=(j)=>s.substr(j,1);c=b(i);d=b(i+1);e=n[c+d];if(e){a+=e;i++}else{a+=n[c]}}alert(a);

The ungolfed code:

n = {
    I: 1,
    V: 5,
    X: 10,
    L: 50,
    C: 100,
    D: 500,
    M: 1e3,
    IV: 4,
    IX: 9,
    XL: 40,
    XC: 90,
    CD: 400,
    CM: 900
}, s = prompt(), a = i = 0;
for (; i < s.length; i++) {
    b = (j) => s.substr(j, 1);
    c = b(i);
    d = b(i + 1);
    e = n[c + d];
    if (e) {
        a += e;
        i++
    } else {
        a += n[c]
    }
}
alert(a);

How this works: first, you store all values inside an object. Then, you prompt for input and you loop over all characters in the string. If the current character + the next character are a number (CM for example), then look up current char + next char in the object. If current char + next char isn't available in the object, just look up the current char.

Demo: http://jsfiddle.net/vSLfM/

ProgramFOX

Posted 2013-12-27T15:20:16.113

Reputation: 8 017

1

Ruby, 134

i=gets
%w[IV,4 IX,9 XL,40 XC,90 CD,400 CM,900 I,1 V,5 X,10 L,50
C,100 D,500 M,1000].map{|s|k,v=s.split ?,;i.gsub!(k,?++v)}
puts eval i

Darren Stone

Posted 2013-12-27T15:20:16.113

Reputation: 5 072