Skip to content

Instantly share code, notes, and snippets.

@molarmanful
Last active September 26, 2018 08:02
Show Gist options
  • Save molarmanful/e3ba0bdd61d1e3e5c4a8da2fbf26e671 to your computer and use it in GitHub Desktop.
Save molarmanful/e3ba0bdd61d1e3e5c4a8da2fbf26e671 to your computer and use it in GitHub Desktop.
Recovered from my now-defunct blog.

ES7 Code Golf Progression

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.

INITIAL PROGRAM

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.

FIRST GOLFS

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){...}

BIG GOLF #1

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.

FINAL GOLFS

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 + -
              )
    )
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment