Dames, do some math!

19

1

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.

Stewie Griffin

Posted 2015-10-22T11:48:29.497

Reputation: 43 471

2

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!

– Dom Hastings – 2015-10-22T13:14:32.027

2Bonus for functions removed, but there is still sin() in the examples. – edc65 – 2015-10-22T18:01:57.433

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. – Jake – 2015-10-22T18:25:37.680

In the UK we are often taught it as BODMAS or BIDMAS at school. B = Brackets, O or I = Order or Indices. – BadHorsie – 2015-10-23T11:45:50.383

Is p needed? It isn't in the examples – ev3commander – 2015-10-23T19:10:37.620

@ev3commander. It sais in the start that the parentheses are always first. – Stewie Griffin – 2015-10-24T07:43:15.000

What's the precedence of unary signing? – theonlygusti – 2017-02-05T13:05:21.497

Answers

7

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>

edc65

Posted 2015-10-22T11:48:29.497

Reputation: 31 086

I think you can remove the space in (t=>t=='('?(z=1, Q.push('_')), along with all the newlines. – Conor O'Brien – 2015-10-23T11:14:49.730

1@CᴏɴᴏʀO'Bʀɪᴇɴ working on it. Thanks – edc65 – 2015-10-23T16:57:52.123

I think you can change the Math.pow(a,b) to a**b – user41805 – 2017-02-05T13:15:26.837

@KritixiLithos yes but it would not be ES6 anymore – edc65 – 2017-02-05T16:11:03.370

6

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

Patrick Roocks

Posted 2015-10-22T11:48:29.497

Reputation: 161