19
\$\begingroup\$

The order of operations, PEMDAS, is a basic rule in mathematics telling us which order operations should be performed:

"Parentheses, Exponents, Multiplication and Division, and Addition and Subtraction"

The problem is, PEMDAS is not very versatile! What if you wanted to do it in another order? We won't mess with the parentheses, so we keep them where they are (first).

Create a program that takes two arguments:

  • A string, telling which order the operations should follow. Some examples are "DAMES", "SAD, ME", "ME SAD", "MEADS". Yes, spaces and commas are OK, as it makes the order easier to remember.
    • Following suggestions in chat: Supporting spaces and commas is now optional.
    • If one of the letters is missing, or if there are additional letters that shouldn't be there, you can consider the input invalid and treat it however you like.
  • A string, or an expression containing the expression that should be evaluated.

Return the result of the expression as either a decimal number, or an integer. If the answer is not an integer, it has to be returned as a decimal number.

Rules:

  • It's OK to combine the two input arguments into one, if that's easier in your language.
  • It doesn't have to be a string, but it has to have letters. You can't substitute Addition with 1, Division with 2, etc.
  • You can choose which input is first.
  • The expression is evaluated right to left left to right. (Change of rule. Any submissions poster the first 12 hours that have this the other way around are accepted).
  • The operations use the symbols: ( ) ^ * / + -. For instance, you can't use ¤ instead of + for addition.
  • Spaces in the input expression is not valid as input
  • Unary +/- is not valid as input if it directly follows + or -. Consider 3+-2 as invalid input. It can be treated however you like (doesn't have to produce an error). If + or - follows any other operator than plus or minus, it's treated the usual way: 3*-3 = -9, sin(-2)=-0.909
  • The program must strictly follow the letters, so "EMDAS", 1-3+4 => -6, and "EMDSA", 1-3+4 => 2.

Examples:

Input:   "EMDAS", "3+6*2/4-1"   // -> 3+12/4-1 -> 3+3-1 -> 6-1 -> 5
Output:  5

Input:   "DAMES", "3+6*2/4-1"   // -> 3+6*0.5-1 -> 9*0.5-1 -> 4.5-1 -> 3.5
Output:  3.5

Input:   "SAD, ME", "3+6*2/4-1"  // -> 3+6*2/3 -> 9*2/3 -> 9*0.66667 -> 6   
Output:  6

Input:   "ME ADS", "3+5^4/2-3*2 // -> 3+5^4/2-6 -> 3+625/2-6 -> 628/2-6 -> 314-6 -> 308
Output:  308

Input:   "AM EDS", "4*3-sin(0.5^2)*3+1" // -> 4*3-sin(0.5^2)*4 -> 12-sin(0.5^2)*4 -> 4*3-(4*sin(0.5^2)) -> 12-(4*sin(0.5^2)) -> 12-(4*sin(0.25)) -> 12-(4*0.24740) -> 12-0.98961 -> 11.01038
Output:  11.01038

Input:   "DAMES", "4-5-6"   // -> (4-5)-6 -> = -7  
Output:  -7                  // NOT: -> 4-(5-6) -> 4-(-1) -> 5

Note, the parentheses where added to show that the multiplication 4*sin(0.5^2) is evaluated before the exponentiation.

This is code golf, so the shortest code in bytes win.

\$\endgroup\$
  • 2
    \$\begingroup\$ It's not exactly the same at all but this challenge is about changing to another order of operations and was the inspiration that made me like the idea of doing something similar. I think the Haskell answer could be re-worked to answer this question perhaps... Not sure if a strict duplicate, I quite like the idea of doing this challenge without the native ability to change the operators directly! \$\endgroup\$ – Dom Hastings Oct 22 '15 at 13:14
  • 2
    \$\begingroup\$ Bonus for functions removed, but there is still sin() in the examples. \$\endgroup\$ – edc65 Oct 22 '15 at 18:01
  • \$\begingroup\$ Somewhat more evil than the aforementioned challenge, and I'm not going to contest it as duplicate (though a link to the original would have been appreciated). However, it is plain for everyone to see the evil director of The 2560 is none other than @Stewie Griffin. I have to say, I'm not surprised. \$\endgroup\$ – JArkinstall Oct 22 '15 at 18:25
  • \$\begingroup\$ In the UK we are often taught it as BODMAS or BIDMAS at school. B = Brackets, O or I = Order or Indices. \$\endgroup\$ – BadHorsie Oct 23 '15 at 11:45
  • \$\begingroup\$ Is p needed? It isn't in the examples \$\endgroup\$ – ev3commander Oct 23 '15 at 19:10
7
\$\begingroup\$

JavaScript (ES6) 349 353 387 400

... maybe still golfable

This old parser of mine sometimes comes in handy - (already used in other 2 challenges)

E=
(d,x,W=[],Q=['_'],h={'(':1,_:8,')':7},z=1,C=n=>{for(;h[q=Q.pop()]<=h[n];W.push(q=='^'?Math.pow(a,b):eval(`a${q}b`)))a=W.pop(b=W.pop());Q.push(q,n)})=>([...d].map(l=>h[l='+-/*^'['ASDME'.search(l)]]=(d+=!!l),d=1),(x+')').replace(/\D|\d+/g,t=>(u=~~h[t])-1?u-7?u?z&&t=='-'?z=-z:C(t,z=1):(W.push(z*t),z=0):Q.pop(Q.pop(C(t),z=0)):z=!!Q.push('_')),W.pop())

// TEST
console.log=(...x)=>O.innerHTML+=x.join` `+'\n'

console.log(E('MDASE','3+4*5^2'))
console.log(E("EMDAS", "3+6*2/4-1")) // 5
console.log(E("DAMES", "3+6*2/4-1")) //3.5
console.log(E("SAD, ME", "3+6*2/4-1")) // 6
console.log(E("ME ADS", "3+5^4/2-3*2")) // 308
console.log(E("AM EDS", "4*3-sin(0.5^2)*3+1")) // 11.01038 sin not supported
console.log(E("DAMES", "4-5-6")) // -7

// MORE READABLE
U=(d,x,W=[],Q=['_'],h={'(':1,_:8,')':7},z=1,
  C=n=>{
    for(;h[q=Q.pop()]<=h[n];
        W.push(q=='^'?Math.pow(a,b):eval(`a${q}b`)))
      a=W.pop(b=W.pop());
    Q.push(q,n)
  }
)=>(
  [...d].map(l=>h[l='+-/*^'['ASDME'.search(l)]]=(d+=!!l),d=1),
  (x+')').replace(/\D|\d+/g,t=> 
     (u=~~h[t])-1
       ?u-7
         ?u
           ?z&&t=='-'?z=-z:C(t,z=1)
           :(W.push(z*t),z=0)
         :Q.pop(Q.pop(C(t),z=0))
       :(Q.push('_'),z=1)
  ),
  W.pop()
)
<pre id=O></pre>

Ungolfed

Evaluate=(oprec,expr)=>
{
  var tokens = expr.match(/\D|\d+/g).concat(')')
  var t,a,b,v, SignV
  var vstack=[]
  var ostack=['_']
  var op={ '(':8, _: 1, ')':2}
  oprec.match(/\w/g).map((l,p)=>op['+-/*^'['ASDME'.search(l)]]=7-p)
  var OPush=o=>ostack.push(o)
  var OPop=_=>ostack.pop()
  var VPush=v=>vstack.push(v)
  var VPop=v=>vstack.pop()

  var Scan=i=>
  {
    SignV = 1
    for (; t=tokens[i++]; )
    {
      if (t == '(')  
      {
        OPush('_')
        SignV = 1
      }
      else if (t == ')')
      {
        CalcOp(t);
        OPop();
        OPop();
        SignV = 0
      }
      else if (op[t])
      {
        if (SignV && t=='-')
          SignV = -SignV
        else
          CalcOp(t), SignV = 1
      }  
      else
      {
        VPush(SignV*t)
        SignV=0
      }
    }
  }
  var CalcOp=nop=>
  {
    for (; op[po = OPop()] >= op[nop];)
      b=VPop(), a=VPop(), CalcV(a,b,po);
    OPush(po), OPush(nop);
  }
  var CalcV=(a,b,o)=>
  {
//    console.log('CV',a,b,o)
    if (o=='+')
      a+=b
    if (o=='-')
      a-=b
    if (o=='*')
      a*=b
    if (o=='/')
      a/=b
    if (o=='^')
      a=Math.pow(a,b)
    VPush(a)
  }
  Scan(0)

  return VPop()
}

console.log=(...x)=>O.innerHTML+=x.join` `+'\n'

console.log(Evaluate('MDASE','3+4*5^2'))
console.log(Evaluate('EMDAS','3+6*2/4-1')) // 5
console.log(Evaluate("DAMES", "3+6*2/4-1")) //3.5
console.log(Evaluate("SAD, ME", "3+6*2/4-1")) // 6
console.log(Evaluate("ME ADS", "3+5^4/2-3*2")) // 308
console.log(Evaluate("AM EDS", "4*3-sin(0.5^2)*3+1")) // 11.01038 sin not supported
console.log(Evaluate("DAMES", "4-5-6")) // -7
<pre id=O></pre>

\$\endgroup\$
  • \$\begingroup\$ I think you can remove the space in (t=>t=='('?(z=1, Q.push('_')), along with all the newlines. \$\endgroup\$ – Conor O'Brien Oct 23 '15 at 11:14
  • 1
    \$\begingroup\$ @CᴏɴᴏʀO'Bʀɪᴇɴ working on it. Thanks \$\endgroup\$ – edc65 Oct 23 '15 at 16:57
  • \$\begingroup\$ I think you can change the Math.pow(a,b) to a**b \$\endgroup\$ – Cows quack Feb 5 '17 at 13:15
  • \$\begingroup\$ @KritixiLithos yes but it would not be ES6 anymore \$\endgroup\$ – edc65 Feb 5 '17 at 16:11
6
\$\begingroup\$

R 3.3.2: 209 196 187 177 bytes

The idea is to "misuse" the non-arithmetic operators <, &, |, ~, ? where we know the precedence (see ?Syntax in R - but before the override ;)) and overriding them with the given arithmetic operators. The mapping is according to the desired order of operations.

Spaces and commas in the input are not supported.

Golfed version

f=function(a,b){s=substr;l=list(E='^',M='*',D='/',A='+',S='-');q="<&|~?";for(i in 1:5){x=s(q,i,i);y=l[[s(a,i,i)]];assign(x,.Primitive(y));b=gsub(y,x,b,,,T)};eval(parse(text=b))}

Ungolfed and commented:

f = function(a,b) {
  s = substr
  # All arithmetic operators
  l = list(E = '^', M = '*', D = '/', A = '+', S = '-')
  # Some non-arithmetic R operators in descending precedence
  q = "<&|~?"
  for (i in 1:5) {
    # The substituted symbol
    x = s(q, i, i)
    # The original operator which has to be substituted
    y = l[[s(a, i, i)]]
    # Substitute the operator for the R interpreter
    assign(x, .Primitive(y))
    # Substitute the operator in the input string
    b = gsub(y, x, b, , , T)
  }
  # Parse and evaluate
  eval(parse(text = b))
}

Examples:

> f("EMDAS", "3+6*2/4-1")
[1] 5
> f("DAMES", "3+6*2/4-1")
[1] 3.5
> f("SADME", "3+6*2/4-1")
[1] 6
> f("MEADS", "3+5^4/2-3*2")
[1] 308
> f("AMEDS", "4*3-sin(0.5^2)*3+1")
[1] 11.01038
> f("DAMES", "4-5-6")
[1] -7
\$\endgroup\$

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.