10
\$\begingroup\$

It is the year MDLXVII in a world in which the Roman Empire never fell, and the collapse into the dark ages never occurred. Due to the extended period of Pax Romana, the empire's economic stability enabled technology to progress at a rapid rate.

The Romans have begun to dabble with circuitry, and have invented an ingenious calculator that doesn't require the use of an "equals" button. They call it "Roman Polish Notation"

To make a calculation, they enter their operands first, then the operation.

For example, 100 + 11 * 20 would be C XI XX * +.

Additionally

The Romans have found that they often need to make multiple calculations at the same time, and would prefer for the method to return every value "on the stack" in some sort of array/list/tuple-like structure. (e.g. X I + X I - CC II + would return [11, 9, 202])


The challenge is to develop a calculator program capable of making these computations.

Clarification: Subtractive Notation is required. I hadn't realized it wasn't recognized feature in the Ancient Roman empire. The task was therefore ambiguous, and I apologize.

Minimum Guidelines

  • Your output will be in Arabic Numerals.
  • You only need to convert from Roman Numerals up to 5000.
  • You will need to support +, -, /, * operations (addition, subtraction, division, and multiplication).
  • Whether division is floating point based or integer based is implementation specific. Either works for this challenge.
  • Your output will need to support numbers up to 4 Billion.
  • Shortest answer overall, AND in each language wins. This is a Code Golf Challenge but I love the variety.

In the event of a tie, factors like support for roman numerals above 5000 or additional operations will be considered the earliest submission will win.

\$\endgroup\$
  • 1
    \$\begingroup\$ Can we take input as a list of strings, each have either a Roman number or an operator? \$\endgroup\$ – user202729 Dec 31 '17 at 16:16
  • \$\begingroup\$ can the input be taken in lowercase, or does it have to be uppercase? \$\endgroup\$ – dzaima Dec 31 '17 at 16:22
  • 1
    \$\begingroup\$ @JesseDanielMitchell As a note... try not to change rules and invalidate existing answers. Also, (as usual) I suggest posting in the Sandbox. \$\endgroup\$ – user202729 Jan 1 '18 at 2:42
6
\$\begingroup\$

Python 2 + roman, 118 bytes

from roman import*
s=[]
for i in input().split():s+=[eval(s.pop(-2)+i+s.pop())if i in"+-/*"else`fromRoman(i)`]
print s

Demo

It cannot be tested online because of the module it makes use of, but you can see how to run this here (a full program accepting input from STDIN – an expression with quotes – and printing the output to STDOUT – in the form of a list, the stack). Uses a slightly older version, because I won't bother to create a new GIF for only a few bytes:

Demo GIF

To install the package, you can run the following in the Terminal / Command Line:

pip install roman
\$\endgroup\$
  • 2
    \$\begingroup\$ pyTester/Py.py ಠ_ಠ \$\endgroup\$ – totallyhuman Dec 30 '17 at 22:49
  • \$\begingroup\$ @totallyhuman It's just a dummy project that I made just for this... \$\endgroup\$ – Mr. Xcoder Dec 30 '17 at 22:50
6
\$\begingroup\$

Haskell, 217 bytes

-13 bytes thanks to Bruce Forte. -73 bytes thanks to Ørjan Johansen.

foldl(!)[].words
s@ ~(x:y:z)!n=last$(a n:s):[y`f`x:z|(f,c)<-zip[(+),(-),(*),(/)]"+-*/",n==[c]]
a s=last$0:[n+a(drop(length x)s)|(n,x)<-zip l$words"I IV V IX X XL L XC C CD D CM M",x<=s,x++"Y">s]
l=[1,4,5,9]++map(10*)l

Try it online!

Manual implementation, yay!

\$\endgroup\$
  • 2
    \$\begingroup\$ I cut that down a bit (so close to beating the new Python one...) Try it online! \$\endgroup\$ – Ørjan Johansen Dec 31 '17 at 2:07
  • 1
    \$\begingroup\$ The Python one got cut down too. But if his argument that subtractive notation doesn't need to be supported is upheld, then there are more savings here too. \$\endgroup\$ – Ørjan Johansen Dec 31 '17 at 2:27
  • 1
    \$\begingroup\$ In any case, another 3 bytes off with l=1:4:5:9:map(10*)l. \$\endgroup\$ – ბიმო Dec 31 '17 at 15:03
  • \$\begingroup\$ I remembered a remainder trick I once found for converting Roman numerals, which takes care of subtraction automatically. Try it online! \$\endgroup\$ – Ørjan Johansen Jan 2 '18 at 2:02
3
\$\begingroup\$

SOGL V0.12, 57 bytes

θ{:u=?F!!}F:u=‽0AøF{1"IVXLCDM”GWH∫2%3*⁽*}aa⁵<⌡±;A}a¹∑ι}}⁰

Try it Here!

\$\endgroup\$
2
\$\begingroup\$

JavaScript (Node) + romans + stk-lang, 74 bytes

s=>(R=require)("stk-lang")(s.replace(/\w+/g,R("romans").deromanize)).stack

Returns a list of bigintegers.

Execution

Run the following:

npm install romans
npm install stk-lang
node

Then paste the function. Example:

C:\Users\conorob\Programming\golf-new\roman
λ npm install romans
npm WARN saveError ENOENT: no such file or directory, open 'C:\Users\conorob\Programming\package.json'
npm WARN enoent ENOENT: no such file or directory, open 'C:\Users\conorob\Programming\package.json'
npm WARN Programming No description
npm WARN Programming No repository field.
npm WARN Programming No README data
npm WARN Programming No license field.

+ romans@1.0.0
added 1 package in 0.801s

C:\Users\conorob\Programming\golf-new\roman
λ npm install stk-lang
npm WARN saveError ENOENT: no such file or directory, open 'C:\Users\conorob\Programming\package.json'
npm WARN enoent ENOENT: no such file or directory, open 'C:\Users\conorob\Programming\package.json'
npm WARN Programming No description
npm WARN Programming No repository field.
npm WARN Programming No README data
npm WARN Programming No license field.

+ stk-lang@1.0.0
added 1 package in 0.847s

C:\Users\conorob\Programming\golf-new\roman
λ node
> s=>(R=require)("stk-lang")(s.replace(/\w+/g,R("romans").deromanize)).stack
[Function]
> f=_
[Function]
> f("X I + X I - CC II +").map(e => e.toString())
[ '11', '9', '202' ]
> f("C XI XX * +").map(e => e.toString())
[ '320' ]
> f("MMMM M I - +").map(e => e.toString())
[ '4999' ]
\$\endgroup\$
  • \$\begingroup\$ How many people use a lambda as prompt? \$\endgroup\$ – Stan Strum Jan 1 '18 at 5:59
  • \$\begingroup\$ @StanStrum I like it, and it's the default for terminals like cmder \$\endgroup\$ – Conor O'Brien Jan 1 '18 at 19:57
  • \$\begingroup\$ Didn't know that. Guess I've never deviated from $ and >. Honestly, I like it though \$\endgroup\$ – Stan Strum Jan 2 '18 at 5:21
2
\$\begingroup\$

Dyalog APL, 93 bytes

⎕CY'dfns'
a←⍬⋄{0::{a,←⍵}roman⍵⋄f←⍎'+-÷×'⌷⍨'+-/*'⍳⍵⋄r←f/¯2↑a⋄a↓⍨←¯2⋄a,←r}¨{1↓¨⍵⊂⍨⍵∊' '}' ',⍞⋄a

Try it online!

116 bytes without the roman built-in

\$\endgroup\$
  • \$\begingroup\$ Woah, never saw modified assignment in a golf before \$\endgroup\$ – Zacharý Dec 31 '17 at 17:46
  • \$\begingroup\$ @Zacharý it's the only way I know to modify a variable out of its dfns scope, so it had to be used here. \$\endgroup\$ – dzaima Dec 31 '17 at 18:05
  • \$\begingroup\$ Forgive my ignorance, but what is modified assignment? \$\endgroup\$ – caird coinheringaahing Jan 1 '18 at 1:52
  • \$\begingroup\$ @cairdcoinheringaahing var fn←arr - it's equivalent to var ← var fn arr. Here it's used in multiple places, a,←⍵ being one which appends to the variable a \$\endgroup\$ – dzaima Jan 1 '18 at 1:55
1
\$\begingroup\$

Python 3, 280 206 bytes

N=dict(I=1,V=5,X=10,L=50,C=100,D=500,M=1000)
def d(s):
	n=0
	for v in map(N.get,s):n+=v-n%v*2
	return n
def c(w):
	s=[]
	for t in w.split():s+=[str(d(t)if t[0]in N else eval(s.pop(-2)+t+s.pop()))]
	return s

Try it online!

This time with subtractive notation support. Method c is the main entry point; the other is support.

Edit log:

\$\endgroup\$
  • \$\begingroup\$ You don't need the indentation blocks after if and else. \$\endgroup\$ – Ørjan Johansen Jan 2 '18 at 1:12
  • \$\begingroup\$ Actually, let me offer you this trick I once found: n+=v-n%v*2 \$\endgroup\$ – Ørjan Johansen Jan 2 '18 at 1:28
  • 1
    \$\begingroup\$ You can also combine the two str uses. Try it online! \$\endgroup\$ – Ørjan Johansen Jan 2 '18 at 1:37
0
\$\begingroup\$

JavaScipt (ES6), 152 151 bytes

Saved 1 byte thanks to user202729

p=>p.split` `.map(c=>s.push(eval("+-/*".indexOf(c)+1?(T=s.pop(),s.pop())+c+T:c.replace(/./g,c=>"+"+{I:1,V:5,X:10,L:50,C:100,D:500,M:1e3}[c]))),s=[])&&s

Test cases

f=p=>p.split` `.map(c=>s.push(eval("+-/*".indexOf(c)+1?(T=s.pop(),s.pop())+c+T:c.replace(/./g,c=>"+"+{I:1,V:5,X:10,L:50,C:100,D:500,M:1e3}[c]))),s=[])&&s
console.log(f("C XI XX * +"))
console.log(f("X I + X I - CC II +"))

Explanation (less golfed)

V={I:1,V:5,X:10,L:50,C:100,D:500,M:1e3}     // Values of the roman numerals
p=>(
 s=[],                                      // Initialize the stack
 p.split` `.map(c=>                         // For every part in the input:
  "+-/*".indexOf(c)+1?                      //   If the input is an operator:
   s.push(eval((T=s.pop(),s.pop())+c+T))    //     Evaluate the operator on the top of the stack
  :                                         //   Else (if it is a roman numeral):
   s.push(eval(c.replace(/./g,c=>"+"+V[c])))//     Push the sum of the characters' values
 ),s)                                       // return the stack
\$\endgroup\$
  • \$\begingroup\$ I'm pretty sure that 1e3 also work and save some bytes. \$\endgroup\$ – user202729 Dec 31 '17 at 11:28
0
\$\begingroup\$

Jelly, 82 bytes

ị“+-×÷”;”/v®ṫ-¤®ṖṖ¤;©
4Ḷ⁵*p1,5P€
“IVXLCDM”iЀị¢µIN‘Ṡæ.µ®;©
Ḳµ“+-*/”W€i⁸Ñ⁸Ǥ¹?µ€ṛ®Ḋ

Try it online!

Originally posted in chat.


Explanation:

Because Jelly doesn't have a stack, I put the stack in the register.

When the program starts, the register value ® is 0, which is treated as [0] for the purposes of this program.


ị“+-×÷”;”/v®ṫ-¤®ṖṖ¤;©       Link 1: Given an operator index (an
                            integer in range 1..4), apply it.

ị“+-×÷”                     Index to the string "+-×÷"
       ;”/                  Concatenate with the character "/",
                            which is Jelly splat operator.
          v   ¤             Evaluate with parameter...
           ®                  the register's
            ṫ                 tail
             -                from -1. (2 last items)
               ®  ¤;        Concatenate with the register value,
                ṖṖ            pop twice.
                    ©       Store the result to register.

4Ḷ⁵*p1,5P€          Link 2: Niladic, generate [1,5,10,50,...]
4Ḷ                  Lowered range of 4, gives [0,1,2,3].
  ⁵*                Raise to power of 10. Value = 1,10,100,1000.
    p1,5            Calculate Cartesian product with [1,5].
                      Value = [1,1],[1,5],[10,1],[10,5],...
        P€          Calculate product of each item.

Alternatively, ×þ1,5F would also work instead of p1,5P€.

“IVXLCDM”iЀị¢µIN‘Ṡæ.µ®;©   Link 3: Given roman number, push it
                            to the stack (register).
         i                  Find index of ...
          Ѐ                  each character ...
“IVXLCDM”                     in "IVXLCDM".
            ị¢              Index to last link. (link 2)
              µ             With that value, (consider LIX ->
                            [50,1,10] for example)
               I             
Ḳµ“+-*/”W€i⁸Ñ⁸Ǥ¹?µ€ṛ®Ḋ

[TODO complete explanation]

\$\endgroup\$
-1
\$\begingroup\$

Python 3, 216 187 bytes

from operator import*
N=dict(I=1,V=5,X=10,L=50,C=100,D=500,M=1000)
def f(w):
	s=[]
	for t in w.split():s+=[str(sum(map(N.get,t)))if t[0]in N else str(eval(s.pop(-2)+t+s.pop()))]
	return s

Try it online!

Because it came up in the comments of both the question and this answer and likely led to down-votes: this submission doesn't support subtractive notation. Rationale: Subtractive notation was rarely used in the Roman Empire and only popularised later (see Subtractive Notation, paragraph 3, last sentence). The task presumes a Roman Empire that developed programmable integrated circuits, not one that underwent the same cultural changes as 13th century Europe. The description doesn't mention subtractive notation and none of the examples uses it.

\$\endgroup\$
  • \$\begingroup\$ Hm... you're not supporting numbers like CIV (104). \$\endgroup\$ – Ørjan Johansen Dec 31 '17 at 2:13
  • \$\begingroup\$ ...can't fault your logic there. :P \$\endgroup\$ – Ørjan Johansen Dec 31 '17 at 2:23
  • 2
    \$\begingroup\$ Agh, you were right. I hadn't thought about the possible ambiguity, I wasn't aware subtractive notation wasn't a common feature in the Ancient Roman empire. \$\endgroup\$ – Jesse Daniel Mitchell Dec 31 '17 at 16:11
  • 1
    \$\begingroup\$ I did actually consider asking about subtractive notation under the OP (and noticed the lack of an example), but got distracted. If you think of definition ambiguities in future challenges, don't hesitate, just ask (answering with a caveat and a link to your comment should do if you want to post). Now a ruling is in you should attempt to fix it up :) \$\endgroup\$ – Jonathan Allan Dec 31 '17 at 18:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.