删除不必要的括号


32

您会得到一个由字符组成的字符串0123456789+*()。您可以假定字符串始终是有效的数学表达式。

您的任务是删除不必要的括号,假设乘法的优先级高于加法。

仅在结构上不需要括号时,才应删除括号:

  • 由于乘法具有更高的优先级:3+(4*5)=>3+4*5
  • 由于乘法或加法关联:3*(4*5)=>3*4*5
  • 当它们在表达式周围多余时:3*((4+5))=>3*(4+5)

由于特定的数字值,在可以简化时应保留括号:

  • 1*(2+3) 不应该简化为 1*2+3
  • 0*(1+0) 不应该简化为 0*1+0

例子:

(4*12)+11         ==>    4*12+11
(1+2)*3           ==>    (1+2)*3
3*(4*5)           ==>    3*4*5
((((523))))       ==>    523
(1+1)             ==>    1+1
1*(2*(3+4)*5)*6   ==>    1*2*(3+4)*5*6
1*(2+3)           ==>    1*(2+3)
0*(1+0)           ==>    0*(1+0)


(((2+92+82)*46*70*(24*62)+(94+25))+6)    ==>    (2+92+82)*46*70*24*62+94+25+6

1
请问更多的测试用例?
Leaky Nun

2
1*(2*(3+4)*5)*6应该是一个有趣的测试用例(我的解决方案当前无法实现)。
Leaky Nun

8
是在结构上还是在个案基础上定义“不必要的” ?换句话说,括号在这里是不必要的吗?(2+2)*1
Luis Mendo

2
@LuisMendo我认为以任何一种方式解释它都是公平的
anatolyg

2
@anatolyg我认为这不公平,因为两者的方法会有很大不同。如果得到一些澄清,那将是很好。
Sp3000 '16

Answers:


15

Mathematica,105 97 91字节

-6个字节感谢Roman

a=StringReplace;ToString@ToExpression@a[#,{"*"->"**","+"->"~~"}]~a~{" ** "->"*","~~"->"+"}&

分别用()和()替换+和,对其求值,对其进行字符串化,然后将运算符替换回来。*~~StringExpression**NonCommutativeMultiply


什么?Mathematica没有内置的?
暴民埃里克

@EriktheGolfer基本上可以做到;我试图使其评估运营商。
LegionMammal978

这就是为什么Mathematica如此宣传和如此昂贵的原因……因为我认为内置功能。但是,如果这个难题足够困难,M​​athematica不会比其他语言有任何变化,但是“其他语言”在这里根本就不存在竞争。
暴民埃里克(Erik the Outgolfer)

通过使用StringExpression代替Dot并删除以下" "->""子句来获取91个字节a=StringReplace;ToString@ToExpression@a[#,{"*"->"**","+"->"~~"}]~a~{" ** "->"*","~~"->"+"}&
罗马

@罗马谢谢!看来您发现了另一个不与数字结合的良好的非交换非求值运算符。
LegionMammal978

7

JavaScript的(ES6)163 178

编辑保存的15个字节thx @IsmaelMiguel

a=>eval(`s=[]${_=';for(b=0;a!=b;a=b.replace(/'}\\(([^()]*)\\)(?=(.?))/,(x,y,z,p)=>~y.indexOf('+')?-s.push(b[p-1]=='*'|z=='*'?x:y):y))b=a;${_}-\\d+/,x=>s[~x]))b=a`)

少打高尔夫球

a=>{
  for(s=[],b='';
      a!=b;
      a=b.replace(/\(([^()]*)\)(?=(.?))/,(x,y,z,p)=>y.indexOf('+')<0?y:-s.push(b[p-1]=='*'|z=='*'?x:y)))
    b=a;
  for(b=0;
      a!=b;
      a=b.replace(/-\d+/,x=>s[~x]))
    b=a;
  return a
}

测试

f=a=>eval(`s=[]${_=';for(b=0;a!=b;a=b.replace(/'}\\(([^()]*)\\)(?=(.?))/,(x,y,z,p)=>~y.indexOf('+')
?-s.push(b[p-1]=='*'|z=='*'?x:y)
:y))b=a;${_}-\\d+/,x=>s[~x]))b=a`)

console.log=x=>O.textContent+=x+'\n'

test=`(4*12)+11         ==>    4*12+11
(1+2)*3           ==>    (1+2)*3
3*(4*5)           ==>    3*4*5
((((523))))       ==>    523
(1+1)             ==>    1+1
1*(2*(3+4)*5)*6   ==>    1*2*(3+4)*5*6
1*(2+3)           ==>    1*(2+3)
0*(1+0)           ==>    0*(1+0)
(((2+92+82)*46*70*(24*62)+(94+25))+6)    ==>    (2+92+82)*46*70*24*62+94+25+6`

test.split`\n`.forEach(r=>{
  var t,k,x
  [t,,k]=r.match(/\S+/g)
  x=f(t)
  console.log((x==k?'OK ':'KO ')+t+' -> '+x+(x==k?'':' expected '+k))
})
<pre id=O></pre>


你为什么写y.indexOf('+')而不是y.indexOf`+`[...]?(为避免格式化而添加[...],是否以此方式进行调试?
伊斯梅尔·米格尔

1
这是170个字节,您可以访问:a=>eval(`for(b=s=[]${_=';a!=b;a=b.replace(/'}\\(([^()]*)\\)(?=(.?))/,(x,y,z,p)=>~y.indexOf('+')<0?-s.push(b[p-1]=='*'|z=='*'?x:y):y))b=a;for(b=0${_}-\\d+/,x=>s[~x]))b=a`)
Ismael Miguel

@IsmaelMiguel真的很聪明,谢谢!经验教训:评估之后,请重新考虑所有内容
edc65 '16

我很高兴您喜欢我的简单解决方案来减少代码。我希望我能做些什么for(b==='*'和其他重复的比特。另外,~y.indexOf('+')<0~y.indexOf('+')吗?由于indexOf()返回的唯一值评估为伪造的值为-1,因此<0似乎是多余的。或者,如果我弄错了,则可以这样做y.indexOf('+')>1
Ismael Miguel

@IsmaelMiguel 1:是的,<0ungolfed版本中剩下的是废话,应该删除。2:再三考虑,for可以将其修改为重复的部分。再次感谢
edc65 '16

5

python中的Python3 + PEG实现,271字节

import peg
e=lambda o,m=0:o.choice and str(o)or(m and o[1][1]and"("+e(o[1])+")"or e(o[1]))if hasattr(o,"choice")else o[1]and e(o[0],1)+"".join(str(x[0])+e(x[1],1)for x in o[1])or e(o[0])
print(e(peg.compile_grammar('e=f("+"f)*f=p("*"p)*p="("e")"/[0-9]+').parse(input())))

不久前,我在Python中实现PEG实现。我想我可以在这里使用它。

将表达式解析为树,并且仅在子代为加法且父代为乘法的情况下保留括号。


4

Perl,132个字节

129个字节的源+ 3个-p标志:

#!perl -p
0while s!\(([^\(\)]+)\)!$f{++$i}=$1,"_$i"!e;s!_$i!($v=$f{$i})=~/[+]/&&($l.$r)=~/[*]/?"($v)":$v!e
while($l,$i,$r)=/(.?)_(\d+)(.?)/

使用:

echo "1*(2*(3+4)*5)*6" | perl script.pl

4

红宝石,140个 130字节

127个字节的源+ 3个-p标志:

t={}
r=?%
0while$_.gsub!(/\(([^()]+)\)/){t[r+=r]=$1;r}
0while$_.gsub!(/%+/){|m|(s=t[m])[?+]&&($'[0]==?*||$`[/\*$/])??(+s+?):s}

和无高尔夫球场:

tokens = Hash.new
key = '%'

# convert tokens to token keys in the original string, innermost first
0 while $_.gsub!(/\(([^()]+)\)/) { # find the innermost parenthetical
  key += key # make a unique key for this token
  tokens[key] = $1
  key # replace the parenthetical with the token key in the original string
}

# uncomment to see what's going on here
# require 'pp'
# pp $_
# pp tokens

# convert token keys back to tokens, outermost first
0 while $_.gsub!(/%+/) {|key|
  str = tokens[key]
  if str['+'] and ($'[0]=='*' or $`[/\*$/]) # test if token needs parens
    '(' + str + ')'
  else
    str
  end
}
# -p flag implicity prints $_

非常好的答案。0 while语法发生了什么?
约拿

1
@Jonah在Ruby expr while cond中等效于while cond; expr; end。在这里,我只想cond重复执行,实际上没有循环体。通常一个会写这是while cond; end或许loop{ break unless cond }不过0 while cond是少字节。在0没有做任何事情; 它就在那里,因为while循环的简短形式需要一个主体。
ezrast

2

视网膜,155字节

{`\(((\d+|\((((\()|(?<-5>\))|[^()])*(?(5)^))\))(\*(\d+|\((((\()|(?<-10>\))|[^()])*(?(10)^))\)))*)\)
$1
(?<!\*)\((((\()|(?<-3>\))|[^()])*(?(3)^))\)(?!\*)
$1

在线尝试!

一次验证所有测试用例。

说明

最主要的是以下代码:

(((\()|(?<-3>\))|[^()])*(?(3)^)

此正则表达式可以匹配括号中的任何字符串,例如1+(2+(3))+42+3

为了便于说明,将此正则表达式设为B

另外,让我们用<>代替的支架,以及pm用于\+\*

代码变为:

{`<((\d+|<B>)(m(\d+|<B>))*)>
$1
(?<!m)<B>(?!m)
$1

前两行与仅包含乘法(例如(1*2*3)或)的方括号匹配(1*(2+3)*4)。它们由内部的内容替换。

最后两行匹配不带括号且不带乘法的括号。它们由内部的内容替换。

最初的{`意思是“直到等幂为止”,这意味着替换将一直进行到它们不再匹配或被自己替换为止。

在这种情况下,替换将一直进行到不再匹配为止。


失败1*(2*(3+4)*5)*6
orlp

@orlp谢谢,固定。
Leaky Nun

失败的(1*(2+3)+4)*5
SP3000

@ Sp3000谢谢,已修复。
Leaky Nun

2

Python 3中,274个 269 359 337 336字节

此方法基本上删除了所有可能的括号对,并检查它是否仍然对其求值。

from re import *
def f(x):
    *n,=sub('\D','',x);x=sub('\d','9',x);v,i,r,l=eval(x),0,lambda d,a,s:d.replace(s,"?",a).replace(s,"",1).replace("?",s),lambda:len(findall('\(',x))
    while i<l():
        j=0
        while j<l():
            h=r(r(x,i,"("),j,")")
            try:
                if eval(h)==v:i=j=-1;x=h;break
            except:0
            j+=1
        i+=1
    return sub('9','%s',x)%tuple(n)

测试线束

print(f("(4*12)+11")=="4*12+11")
print(f("(1+2)*3") =="(1+2)*3")
print(f("3*(4*5)")=="3*4*5")
print(f("((((523))))")=="523")
print(f("(1+1)")=="1+1")
print(f("1*(2*(3+4)*5)*6")=="1*2*(3+4)*5*6")
print(f("(((2+92+82)*46*70*(24*62)+(94+25))+6)")=="(2+92+82)*46*70*24*62+94+25+6")
print(f("1*(2+3)")=="1*(2+3)")
print(f("0*(1+0)")=="0*(1+0)")

更新

  • -1 [16-10-04]删除了多余的空间
  • -22 [16-05-07]利用了relib
  • +90 [16-05-07]已更新,可以处理新的测试用例
  • -5 [16-05-07]从长度(l)lambda中删除了参数

1
这使测试用例失败1*(2+3),因为OP表示不简化特殊情况。很好的答案;这是我的投票。
HyperNeutrino,2016年

1
@AlexL。感谢您抓住这一点!我没有更新测试用例D:但现在已修复。
NonlinearFruit

1

PHP,358字节

function a($a){static$c=[];$d=count($c);while($g=strpos($a,')',$g)){$f=$a;$e=0;for($j=$g;$j;--$j){switch($a[$j]){case')':++$e;break;case'(':--$e;if(!$e)break 2;}}$f[$g++]=$f[$j]=' ';if(eval("return $f;")==eval("return $a;"))$c[str_replace(' ', '', $f)]=1;}if(count($c)>$d){foreach($c as$k=>$v){a($k);}}return$c;}$t=a($argv[1]);krsort($t);echo key($t);

这不是一个令人印象深刻的长度,这就是我采用的方法不是最佳方法(并且使用的语言不是最佳方法)。

去除括号,然后评估结果表达式。如果结果与原始结果相同,则会将其添加到有效表达式的映射中,然后递归直到找不到新的表达式。然后输出最短的有效表达式。

当表达式的结果变大并强制转换为double /指数表示法时中断。


1

序言(SWI) 122个 118字节

T+S:-term_string(T,S).
I/O:-X+I,X-Y,Y+O.
E-O:-E=A+(B+C),A+B+C-O;E=A*(B*C),A*B*C-O;E=..[P,A,B],A-X,B-Y,O=..[P,X,Y];E=O.

在线尝试!

定义一个谓词//2,该谓词从其第一个参数的字符串值中删除括号,并通过其第二个参数输出字符串。如果输入可以用Prolog术语表示,则仅需81 77字节即可定义+/2而不必处理冗长的内容term_string/2,但是从这种方式开始根本就不存在很多不必要的括号,因此它几乎很容易作弊,因为所有+/2要做的就是处理关联性。

我尝试将=../2其全部使用,但它的出现时间更长,因为与列表配合使用的三字节运算符并不十分简洁:

Prolog(SWI),124字节

T+S:-term_string(T,S).
I/O:-X+I,X-Y,Y+O.
X-Y:-X=..[O,A,B],(B=..[O,C,D],E=..[O,A,C],F=..[O,E,D],F-Y;A-C,B-D,Y=..[O,C,D]);X=Y.

在线尝试!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.