Remova parênteses desnecessários


32

Você recebe uma sequência composta pelos caracteres 0123456789+*(). Você pode assumir que a string é sempre uma expressão matemática válida.

Sua tarefa é remover os parênteses desnecessários, assumindo que a multiplicação tenha maior prioridade que a adição.

Os parênteses devem ser removidos somente quando não forem necessários estruturalmente :

  • por causa da multiplicação maior prioridade: 3+(4*5)=>3+4*5
  • por causa da multiplicação ou adição de adição: 3*(4*5)=>3*4*5
  • quando são redundantes em torno de uma expressão: 3*((4+5))=>3*(4+5)

Os parênteses devem ser mantidos quando puderem ser simplificados devido a valores numéricos específicos:

  • 1*(2+3) não deve ser simplificado para 1*2+3
  • 0*(1+0) não deve ser simplificado para 0*1+0

Exemplos:

(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

11
Mais casos de teste, por favor?
Freira vazando 05/05

2
1*(2*(3+4)*5)*6deve ser um caso de teste interessante (para o qual minha solução atualmente falha).
Freira vazando 05/05

8
"Desnecessário" é definido estruturalmente ou caso a caso? Em outras palavras, os parênteses são desnecessários aqui? (2+2)*1
Luis Mendo 5/05

2
@LuisMendo eu acho que é justo para interpretá-lo em qualquer forma
anatolyg

2
@ anatolyg Eu não acho que seria justo, porque as abordagens para os dois seriam muito diferentes. Seria bom se tivéssemos alguns esclarecimentos.
Sp3000

Respostas:


15

Mathematica, 105 97 91 bytes

-6 bytes graças a Roman !

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

Substitui +e *por ~~( StringExpression) e **( NonCommutativeMultiply), respectivamente, avalia, especifica e substitui os operadores novamente.


O que? Mathematica não tem um built-in?
Erik the Outgolfer

@EriktheGolfer Basicamente, sim; Estou tentando fazer com que não avalie os operadores.
LegionMammal978

É por isso que o Mathematica é tão anunciado e tão caro ... por causa dos embutidos, eu acho. Mas, o Mathematica não mudou em relação a outros idiomas, se o quebra-cabeça é difícil o suficiente, mas "outros idiomas" não competem aqui.
Erik the Outgolfer

91 bytes usando em StringExpressionvez de Dote removendo a " "->""cláusula:a=StringReplace;ToString@ToExpression@a[#,{"*"->"**","+"->"~~"}]~a~{" ** "->"*","~~"->"+"}&
Roman

@ Roman Obrigado! Parece que você encontrou outro bom operador não avaliativo associativo não comutativo que não combina com números.
LegionMammal978 17/06

7

JavaScript (ES6) 163 178

Editar 15 bytes salvos 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`)

Menos golfe

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
}

Teste

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>


Por que você escreveu em y.indexOf('+')vez de y.indexOf`+`[...]? (adicionado [...] para evitar tropeçar na formatação) Foi desse jeito?
Ismael Miguel

11
Aqui está, 170 bytes: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 isso é realmente inteligente, obrigado! Lição aprendida: ao passar a eval, repensar tudo de novo
edc65

Fico feliz que você tenha gostado da minha solução simples para reduzir seu código. Eu gostaria de poder fazer algo sobre for(b=, =='*'e outros bits repetidos. Além disso, não é ~y.indexOf('+')<0o mesmo que ~y.indexOf('+')? Como o único valor indexOf()retornado que é avaliado como falso -1, é <0redundante. Ou, se eu entendi errado, você poderia fazery.indexOf('+')>1
Ismael Miguel

@IsmaelMiguel 1: sim, a <0porcaria remanescente da versão não destruída e deve ser removida. 2: pensando novamente, o forpode ser revisado para ser incluído na parte repetida. Obrigado novamente
edc65

5

Implementação Python3 + PEG em Python , 271 bytes

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())))

Há um tempo atrás eu fiz uma implementação PEG em Python . Eu acho que posso usar isso aqui.

Analisa a expressão em uma árvore e só mantém parênteses se o filho for adição e o pai for multiplicação.


4

Perl, 132 bytes

Origem de 129 bytes + 3 para o -psinalizador:

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

Usando:

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

4

Ruby, 140 130 bytes

Origem de 127 bytes + 3 para o -psinalizador:

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

E não destruído:

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 $_

resposta muito boa. o que está acontecendo com a 0 whilesintaxe?
Jonah

11
@ Jonah Em Ruby, expr while condé equivalente a while cond; expr; end. Aqui, eu só quero executar condrepetidamente e na verdade não tenho um corpo em loop. Normalmente, alguém escreveria isso como while cond; endou talvez, loop{ break unless cond }mas 0 while condtem menos bytes. O 0não faz nada; está lá porque a forma curta do loop while requer um corpo.
ezrast 17/06

2

Retina, 155 bytes

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

Experimente online!

Verifique todos os casos de teste de uma só vez.

Explicação

O principal é este código:

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

Essa regex pode corresponder a qualquer sequência na qual os colchetes estejam balanceados, por exemplo, 1+(2+(3))+4ou 2+3.

Para facilitar a explicação, deixe esse regex B.

Além disso, vamos usar <e >para os colchetes, bem como pe mpara \+e \*.

O código se torna:

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

As duas primeiras linhas correspondem a colchetes que consistem apenas em multiplicação, por exemplo, (1*2*3)ou mesmo (1*(2+3)*4). Eles são substituídos pelo conteúdo interno.

As duas últimas linhas correspondem a colchetes que não são precedidos e que não são seguidos por multiplicação. Eles são substituídos pelo conteúdo interno.

O inicial {`significa "substituir até idempotente", significando que as substituições são feitas até que não correspondam mais ou sejam substituídas por elas mesmas.

Nesse caso, as substituições são feitas até que não correspondam mais.


Falha em 1*(2*(3+4)*5)*6.
Orlp 05/05

@orlp Obrigado, corrigido.
Freira vazando 05/05

Não consegue(1*(2+3)+4)*5
Sp3000 5/05

@ Sp3000 Obrigado, corrigido.
Freira vazando 05/05

2

Python 3, 274 269 359 337 336 bytes

Esse método basicamente remove todos os parênteses possíveis e verifica se ele ainda avalia o mesmo.

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)

Equipamento de teste

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)")

Atualizações

  • -1 [16-10-04] Removido espaço extra
  • -22 [16-05-07] Utilizou a relib
  • +90 [16-05-07] Atualizado para lidar com os novos casos de teste
  • -5 [16-05-07] Removido o parâmetro do comprimento ( l) lambda

11
Isso falha no caso de teste 1*(2+3), porque o OP disse para não simplificar para casos especiais de números. Boa resposta embora; isso tem o meu voto positivo.
HyperNeutrino

11
@AlexL. Obrigado por capturar isso! Não atualizei meus casos de teste D: Mas está corrigido agora.
NonlinearFruit

1

PHP, 358 bytes

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);

Não é uma duração impressionante, é o que recebo por adotar uma abordagem menos que ótima (e usar uma linguagem que não seja ótima).

Retira um par de colchetes e avalia a expressão resultante. Se o resultado for o mesmo que o original, ele será adicionado a um mapa de expressões válidas e se repetirá até que nenhuma nova expressão seja encontrada. Em seguida, imprime a expressão válida mais curta.

Quebra quando o resultado da expressão aumenta e lança a notação de duplo / expoente.


1

Prolog (SWI) , 122 118 bytes

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.

Experimente online!

Define um predicado //2que remove parênteses do valor da string do primeiro argumento e gera uma string através do segundo argumento. Se a entrada pudesse ser em termos de prólogo, isso teria apenas 81 77 bytes de definição +/2sem ter que lidar com os verbos detalhados term_string/2, mas muitos parênteses desnecessários simplesmente não existiriam para começar dessa maneira, portanto seria bem próximo de trapacear, pois tudo o que +/2faz é lidar com associatividade.

Tentei usar =../2para tudo isso, mas ficou muito mais tempo, porque um operador de três bytes que trabalha com listas não é exatamente conciso:

Prolog (SWI) , 124 bytes

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.

Experimente online!

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.