This time, I have a program that takes numerical expressions with the parentheses, exponentiation, multiplication, division, addition, and subtraction and outputs each successive step used to solve the expression using order of operations. Example inputs:
1+1
(51*5)/8
63/(91*(5+779))
Spaces are the only types of whitespace allowed.
Before I start, I almost always think about the algorithm I will use to solve the problem:
- Take an input using the browser prompt, cleaning out whitespace.
- If there are parentheses, jump to the innermost nested parentheses and begin evaluation.
- Recursively replace (in order) exponents, multiplication/division, and addition/subtraction in the form of "[number][operation][number]=[result]".
- Replace the innermost parentheses with the result from step 3 and go back to step 2 unless the result is only a number.
- Of course, this algorithm is subject to change, but it should be enough to get going.
i=c=prompt().replace(/\s/g,'')
R=(t,r)=>{
T=t.replace(r,a=>(alert(a+`=`+eval(a)),eval(a)))
return t!=T?R(T,r):t
}
while(isNaN(i)){
z=(x=c.match(/\(([^()]+)\)/))?(c=x[1]):0
c=R(R(R(c,/\d+\*\*\d+/),/\d+[*\/]\d+/),/\d+[+-]\d+/)
i=c=z?i.replace(/\(([^()]+)\)/,c):c
}
I always shorten the variables down before code-golfing to save time and effort. If you have trouble keeping track of the variables, just comment what they do:
// R => recursive replace & output function
// i => input (will become output)
// c => copy of i, used to evaluate innermost parentheses
// z => checks if i has any parentheses left
I also did some light golfing in my initial program to save time later on.
At this point, I'll do some easy saves.
Lambdas can return the last statement, which means this:
R=(t,r)=>(T=t.replace(r,a=>(alert(a+`=`+eval(a)),eval(a))),t!=T?R(T,r):t)
Some rearrangements make the lambda even shorter:
R=(t,r,T=t.replace(r,a=>(alert(a+`=`+eval(a)),eval(a))))=>t!=T?R(T,r):t
Aliasing is useful (both for "replace" and the parentheses regex):
i=c=prompt()[b='replace'](/\s/g,'')
R=(t,r,T=t[b](r,a=>(alert(a+`=`+eval(a)),eval(a))))=>t!=T?R(T,r):t
while(isNaN(i)){
z=(x=c.match(p=/\(([^()]+)\)/))?(c=x[1],1):0
c=R(R(R(c,/\d+\*\*\d+/),/\d+[*\/]\d+/),/\d+[+-]\d+/)
i=c=z?i[b](p=/\(([^()]+)\)/,c):c
}
isNaN(i) can be shortened in this case:
while(!+i){...}
At this point, I see a possible big save in byte count.
b='replace'
i=`(${prompt()[b](/\s/g,'')})`
R=(t,r,o=a=>(alert(a+`=`+eval(a)),eval(a)),T=t[b](r,o))=>t!=T?R(T,r,o):t
R(i,/\(([^()]+)\)/,(X,Y)=>R(R(R(Y,/\d+\*\*\d+/),/\d+[*\/]\d+/),/\d+[+-]\d+/))
Changes made:
- Completely took out the while loop, using the recursive replace to parse the parentheses.
- Modified R to also have a function argument (which defaults to alerting the expression and result).
- Wrapped the input in parentheses before passing it to R to evaluate the last expression correctly.
Aliasing "replace" now costs instead of saves bytes, so I'll take that out:
i=`(${prompt().replace(/\s/g,'')})`
R=(t,r,o=a=>(alert(a+`=`+eval(a)),eval(a)),T=t.replace(r,o))=>t!=T?R(T,r,o):t
R(i,/\(([^()]+)\)/,(X,Y)=>R(R(R(Y,/\d+\*\*\d+/),/\d+[*\/]\d+/),/\d+[+-]\d+/))
Since i is only used one other time, I'll just inline it:
R=(t,r,o=a=>(alert(a+`=`+eval(a)),eval(a)),T=t.replace(r,o))=>t!=T?R(T,r,o):t
R(`(${prompt().replace(/\s/g,'')})`,/\(([^()]+)\)/,(X,Y)=>R(R(R(Y,/\d+\*\*\d+/),/\d+[*\/]\d+/),/\d+[+-]\d+/))
I can save a few bytes using a lambda rather than prompt for input:
I=>(R=(t,r,o=a=>(alert(a+`=`+eval(a)),eval(a)),T=t.replace(r,o))=>t!=T?R(T,r,o):t,R(`(${I.replace(/\s/g,'')})`,/\(([^()]+)\)/,(X,Y)=>R(R(R(Y,/\d+\*\*\d+/),/\d+[*\/]\d+/),/\d+[+-]\d+/)))
I can also replace the second replace with R:
I=>(R=(t,r,o=a=>(alert(a+`=`+eval(a)),eval(a)),T=t.replace(r,o))=>t!=T?R(T,r,o):t,R(`(${R(I,' ','')})`,/\(([^()]+)\)/,(X,Y)=>R(R(R(Y,/\d+\*\*\d+/),/\d+[*\/]\d+/),/\d+[+-]\d+/)))
And that's the final, fully-golfed program at 177 bytes, much shorter than the ~270-byte initial program.
Annotated:
I=>(
R= // recursive replace/expression output
(t, // string to be modified
r, // regex
o=a=>(alert(a+`=`+eval(a)),eval(a)), // replacement function
// defaults to expression output + return eval'd result
T=t.replace(r,o) // the crucial replacement part
)=>t!=T?R(T,r,o):t, // keep replacing t until it has no replacements left
R(`(${R(I,' ','')})`, // take the input, strip all spaces, and wrap in parentheses
/\(([^()]+)\)/, // evaluate expressions in parentheses inside-out
(X,Y)=>R(R(R(Y, // replace innermost parentheses with result of:
/\d+\*\*\d+/), // eval **
/\d+[*\/]\d+/), // eval * /
/\d+[+-]\d+/) // eval + -
)
)