Parenthesize an expression

20

4

Recently I've been writing a new language, to avoid needing to handle order of operations, I simply parenthesize each expression properly to avoid this entirely.

Because parenthesis are at char-codes 40-41, your code will need to be as short as possible.


Examples

1+2*3
(1+(2*3))

2*(3+4)
(2*(3+4))

2*3/4+3
(((2*3)/4)+3)

342*32/8
((342*32)/8)

Rules

The only operations you'll need to handle are: * (multiplication), / (division), + (addition), and - (subtraction).

  • The order of operations is:
    • Parenthesis
    • Multiplication, Division
    • Adition, Subtraction
  • You should prefer to go left-right
  • The input numbers will always be positive integers (see bonuses)

Bonuses

-20% if you handle negation:

3+-5
(3+(-5))

-5% if you allow spaces to be placed inside the input:

3  + 4
(3+4)

-10% if you can handle decimals in the input:

1+.12
(1+.12)
1+0.21/3
(1+(0.21/3))

500 bounty: if you manage to write an answer in Unnamed/Blocks

Downgoat

Posted 2015-12-24T21:02:48.547

Reputation: 27 116

And you want us to write the parser for you? :3 – Conor O'Brien – 2015-12-24T21:04:08.940

25"Because parenthesis are at char codes 40-41, your code will need to be as short as possible." OK, now you're just being plain ridiculous. ;P – ETHproductions – 2015-12-24T21:06:10.683

@ETHproductions I'll award a bounty (500?) if anyone manages to do that but I doubt it's possible because currently it can barely add numbers :/ – Downgoat – 2015-12-24T21:07:57.830

Whoops, accidentally deleted my comment. Here it is for reference: "Do we get a bonus for answering in your new language?" – ETHproductions – 2015-12-24T21:09:33.140

3And this is easier than prefix (Polish) notation because? – wizzwizz4 – 2015-12-24T21:12:36.817

3

Possible duplicate.

– flawr – 2015-12-24T22:02:59.497

8@flawr I saw that but it's very different in the fact that that question has you output all ways parenthesizing an expression. Here you have to take into account order of operations which I think is a significant difference as code can't be trivially modified for this challenge – Downgoat – 2015-12-24T22:20:02.143

3Important test case: 1+2+3+4 (which certain solutions might parenthesise as ((1+2)+(3+4))) – Martin Ender – 2015-12-24T23:25:12.363

1About handling negation: 3 - - - - 6 => (3+6) or (3-(-(-(-6)))) ? – edc65 – 2015-12-28T15:28:20.123

Is it ok to put parenthesis around each number? For example: ((3)+(4)) for 3+4 and ((1)+((2)*(3))) for 1+2*3? – agtoever – 2015-12-29T14:01:10.063

The first paragraph gives some context, but you seem to have forgotten to give the specification. You've told us what you do: what do you want us to do? What input do we take? What processing do we perform on it? – Peter Taylor – 2015-12-30T09:57:02.483

Where can I find specification for Unnamed? – Akangka – 2016-01-02T05:28:37.463

@ChristianIrwan there's not much documentation but there's an esolangs article here and you can look at the source code of the page and look for the <script> tag for source code. I also have an answer on the site here. One tip is that ` is the input and anything in {} in evaluated as JavaScript that's about all I remember

– Downgoat – 2016-01-02T05:34:28.893

@Doᴡɴɢᴏᴀᴛ The esolang article doesn't have specification of command. – Akangka – 2016-01-02T05:42:03.780

I don't read in Javascript. – Akangka – 2016-01-02T05:59:38.250

@ChristianIrwan hmm, sorry about the lack of documentation, I don't even know how the language works anymore but basically is replaced with the input. This means your code is essentially1,1::0,0={JS_CODE}|;0,0essentially sets the first output (Unnamed has 2D output).1,1sets an output to a 1*1 matrix. the|` is the Set command. I'll try to write up some more docs tonight, – Downgoat – 2016-01-02T06:02:44.327

1The "Unnamed" link does not work. – Erik the Outgolfer – 2016-10-06T08:23:22.750

Answers

2

Python, 153 * 0.9 = 137.7 bytes

def p(e):
 for o in"+-*/":
    for i,c in enumerate(e):
        if(c==o)*(0==sum([(d=="(")-(d==")")for d in e[:i]])):return"("+p(e[:i])+o+p(e[i+1:])+")"
 return e

This program handles decimal input.

The second line begins with a space, the second begins with a tab, the third with two tabs and the third with a space. This saved one byte. Here's a hexdump (xxdp p):

0000000: 6465 6620 7028 6529 3a0a 2066 6f72 206f  def p(e):. for o
0000010: 2069 6e22 2b2d 2a2f 223a 0a09 666f 7220   in"+-*/":..for 
0000020: 692c 6320 696e 2065 6e75 6d65 7261 7465  i,c in enumerate
0000030: 2865 293a 0a09 0969 6628 633d 3d6f 292a  (e):...if(c==o)*
0000040: 2830 3d3d 7375 6d28 5b28 643d 3d22 2822  (0==sum([(d=="("
0000050: 292d 2864 3d3d 2229 2229 666f 7220 6420  )-(d==")")for d 
0000060: 696e 2065 5b3a 695d 5d29 293a 7265 7475  in e[:i]])):retu
0000070: 726e 2228 222b 7028 655b 3a69 5d29 2b6f  rn"("+p(e[:i])+o
0000080: 2b70 2865 5b69 2b31 3a5d 292b 2229 220a  +p(e[i+1:])+")".
0000090: 2072 6574 7572 6e20 650a                  return e.

Here's a program I used for testing: (Save the program above as paren.py)

import paren

cases = {
        "2+3*4": "(2+(3*4))", 
        "(2+3)*4": "((2+3)*4)", 
        "1+2+3+4": "(1+(2+(3+4)))", 
        "3/2+5": "((3/2)+5)", 
        "1+2-3": "(1+(2-3))", 
        "2-1+2": "((2-1)+2)",
        "3+-5": "(3+(-5))",
        "1+.12": "(1+.12)",
        "1+0.21/3": "(1+(0.21/3))",
}


for num, case in enumerate(cases):
    print "\n\n\033[1m\033[38;5;14mCase #%d: %s" % (num + 1, case)
    result = paren.p(case)
    print "\033[38;5;4mParenthesize returned: %s" % (result)
    solution = cases[case]
    if result == solution:
        print "\033[38;5;76mCorrect!"
    else:
        print "\033[38;5;9mNot correct!"

Make sure that your terminal uses the \033[38;5;<COL>m escape code for colors.

Loovjo

Posted 2015-12-24T21:02:48.547

Reputation: 7 357

*fourth with a space? – Element118 – 2015-12-28T23:27:51.383

1This program does not prefer to go left-right. Try the test case 3 in the OP, your result is not correct. This can be a real issue for example with integer arithmetic: ((2*(3/4))+3) != (((2*3)/4)+3) – edc65 – 2015-12-29T12:30:49.063

1@user12365 Not using integer arithmetic (in C or C++ for example) 3/4 == 0, so ((2(3/4))+3) is 3, while (((23)/4)+3) is 4 – edc65 – 2015-12-30T14:01:13.400

3

JavaScript (ES6) 179 (263 -20% -5% -10%)

(x,W=[],Q=['('],z=1,w=v='',h=p=>'*/+-))('.indexOf(p)|1,C=n=>{for(;h(q=Q.pop())<=h(n);)W[0]=`(${W[1]+q+W.shift()})`;z&&Q.push(q,n)})=>(x+')').replace(/[\d.]+|\S/g,t=>t>'('?t>')'?~h(t)?z?(w+='('+t,v+=')'):C(t,z=1):W=[w+t+v,...W,z=w=v='']:C(t,z=0):z=Q.push(t))&&W[0]

As the other two answers are currently both wrong, I'll post mine. It's a variation of the expression parser that I used here and here and somewhere else. Look there for more detailed algorithm explanations.

It's quite bulky but it should work.

Test snippet

f=(x,W=[],Q=['('],z=1,w=v='',h=p=>'*/+-))('.indexOf(p)|1,C=n=>{for(;h(q=Q.pop())<=h(n);)W[0]=`(${W[1]+q+W.shift()})`;z&&Q.push(q,n)})=>(x+')').replace(/[\d.]+|\S/g,t=>t>'('?t>')'?~h(t)?z?(w+='('+t,v+=')'):C(t,z=1):W=[w+t+v,...W,z=w=v='']:C(t,z=0):z=Q.push(t))&&W[0]

// More readable
x=(x,W=[],Q=['('],z=1,w=v='',
  h=p=>'*/+-))('.indexOf(p)|1,
  C=n=>{
    for(;h(q=Q.pop())<=h(n);)W[0]=`(${W[1]+q+W.shift()})`;
    z&&Q.push(q,n)
  }
)=>(
  (x+')')
  .replace(/[\d.]+|\S/g,t=> 
       t>'('    
       ?t>')'
       ?~h(t)
       ?z
       ?(w+='('+t,v+=')')
       :C(t,z=1)
       :W=[w+t+v,...W,z=w=v=''] // overfill W to save 2 chars ()
       :C(t,z=0)
       :z=Q.push(t)
  ),
  W[0]
)

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

// TEST
;[
  ['1+2*3','(1+(2*3))'],['2*(3+4)','(2*(3+4))'],['2*3/4+3','(((2*3)/4)+3)'],['342*32/8','((342*32)/8)'],
  ['3+-5','(3+(-5))'],['-3+-4*7','((-3)+((-4)*7))'], // bonus 20%
  ['3  + 4','(3+4)'], // bonus 5%
  ['1+.12','(1+.12)'],['1+0.21/3','(1+(0.21/3))'] // bonus 10%
].forEach(t=>{var k=t[1],i=t[0],r=f(i); console.log(i+' : '+r+(r==k? ' OK':' Fail expecting '+k))})
<pre id=O></pre>

edc65

Posted 2015-12-24T21:02:48.547

Reputation: 31 086

1

Python, 241 * 0.8 * 0.95 * 0.9 = 164.84 characters

I'm using the ast (Abstract Syntax Trees) library, and a homebrew string replacement dict. The string replacement costs a lot, but the bonus helps in keeping the score somewhat low. Maybe (the string replacement part) can be golfed further.

Note that this solution adds an extra set of parenthesis around each number, but I think that's within the spirit of the question

import ast;def p(e):
 r,s={"Module([":"",")])":"","Expr(":"","BinOp":"","Num":"",", Add(), ":"+",", Sub(), ":"-",", Div(), ":"/",", Mult(), ":"*"},ast.dump(ast.parse(e),annotate_fields=False)
 for f,t in r.iteritems():s=s.replace(f,t)
 return s

Test suite:

cases = {
    "2+3*4", 
    "(2+3)*4", 
    "1+2+3+4", 
    "3/2+5", 
    "1+2-3", 
    "2-1+2",
    "3+-5",
    "1+.12",
    "1+0.21/3"
}

for num,case in enumerate(cases):
    result = p(case)
    print "Case {}: {:<16} evaluates to: {}".format(num+1,case,result)

Output of the test suite:

Case 1: 3+-5             evaluates to: ((3)+(-5))
Case 2: 3/2+5            evaluates to: (((3)/(2))+(5))
Case 3: 2+3*4            evaluates to: ((2)+((3)*(4)))
Case 4: 1+2+3+4          evaluates to: ((((1)+(2))+(3))+(4))
Case 5: 1+0.21/3         evaluates to: ((1)+((0.21)/(3)))
Case 6: (2+3)*4          evaluates to: (((2)+(3))*(4))
Case 7: 2-1+2            evaluates to: (((2)-(1))+(2))
Case 8: 1+.12            evaluates to: ((1)+(0.12))
Case 9: 1+2-3            evaluates to: (((1)+(2))-(3))

agtoever

Posted 2015-12-24T21:02:48.547

Reputation: 2 661

Missing import ast in your code – edc65 – 2015-12-30T09:11:06.473

And that is not the right way to compund percent bonus. If you get a discount of 50% and on top of that another of 50%, your are not paying 0. Your score should be 157.32 (something more after adding the import line). That is a good score - I'll upvote if you make the fix – edc65 – 2015-12-30T09:15:36.753

Good point. Added the import. 241 characters now. Not sure how to calculate the bonus though. If I understand your comment correctly, the order in which the bonus is subtracted matters... – agtoever – 2015-12-30T09:31:37.570

The bonus is not subtracted (it's a multiplication) and the order does not matter. 241(1-20%)(1-5%)(1-10%) => 2410.80.950.9 => 164.84 – edc65 – 2015-12-30T09:57:12.793

@edc65 Ah. Right. Wasn't thinking straight. Thanks. – agtoever – 2015-12-30T13:46:30.023