Eliminar paréntesis innecesarios

32

Te dan una cadena compuesta con los caracteres 0123456789+*(). Puede suponer que la cadena siempre es una expresión matemática válida.

Su tarea es eliminar los paréntesis innecesarios, suponiendo que la multiplicación tenga mayor prioridad que la suma.

Los paréntesis deben eliminarse solo cuando no son necesarios estructuralmente :

  • debido a la multiplicación mayor prioridad: 3+(4*5)=>3+4*5
  • debido a la asociatividad de multiplicación o suma: 3*(4*5)=>3*4*5
  • cuando son redundantes alrededor de una expresión: 3*((4+5))=>3*(4+5)

Los paréntesis deben mantenerse cuando puedan simplificarse debido a valores numéricos específicos:

  • 1*(2+3) no debe simplificarse a 1*2+3
  • 0*(1+0) no debe simplificarse a 0*1+0

Ejemplos:

(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
Arnaud
fuente
1
¿Más casos de prueba por favor?
Leaky Nun
2
1*(2*(3+4)*5)*6debería ser un caso de prueba interesante (por el cual mi solución falla actualmente).
Leaky Nun
8
¿Se define "innecesariamente" estructuralmente o según el caso? En otras palabras, ¿los paréntesis son innecesarios aquí? (2+2)*1
Luis Mendo
2
@LuisMendo Creo que es justo interpretarlo de cualquier manera
anatolyg
2
@anatolyg No creo que sea justo, porque los enfoques para los dos serían muy diferentes. Sería bueno si obtuviéramos alguna aclaración.
Sp3000

Respuestas:

15

Mathematica, 105 97 91 bytes

-6 bytes gracias a Roman !

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

Reemplaza +y *con ~~( StringExpression) y **( NonCommutativeMultiply) respectivamente, lo evalúa, lo encadena y reemplaza los operadores.

LegionMammal978
fuente
¿Qué? Mathematica no tiene un incorporado?
Erik the Outgolfer
@EriktheGolfer Básicamente lo hace; Estoy tratando de hacer que no evalúe a los operadores.
LegionMammal978
Es por eso que Mathematica es tan publicitado y tan costoso ... debido a las incorporaciones, creo. Pero, Mathematica no tiene un cambio sobre otros idiomas si el rompecabezas es lo suficientemente difícil, sin embargo, "otros idiomas" no compiten en absoluto aquí.
Erik the Outgolfer
91 bytes usando en StringExpressionlugar de Dot, y eliminando la " "->""cláusula:a=StringReplace;ToString@ToExpression@a[#,{"*"->"**","+"->"~~"}]~a~{" ** "->"*","~~"->"+"}&
Roman
@Roman Gracias! Parece que encontró otro buen operador asociativo no conmutativo no evaluado que no se combina con números.
LegionMammal978
7

JavaScript (ES6) 163 178

Editar 15 bytes guardados gracias a @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 golf

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
}

Prueba

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>

edc65
fuente
¿Por qué escribiste en y.indexOf('+')lugar de y.indexOf`+`[...]? ([...] agregado para evitar tropezar con el formateo) ¿Estaba fallando de esa manera?
Ismael Miguel
1
Aquí tienes, 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 que es muy inteligente, gracias! Lección aprendida: al pasar a evaluar, reconsiderar todo de nuevo
edc65
Me alegra que te haya gustado mi solución simple para reducir tu código. Desearía poder hacer algo al respecto for(b=, =='*'y otras partes repetidas. Además, ¿no es lo ~y.indexOf('+')<0mismo que ~y.indexOf('+')? Dado que el único valor que indexOf()devuelve que se evalúa como un valor falso es -1, el <0parece redundante. O, si me equivoqué, podrías hacerloy.indexOf('+')>1
Ismael Miguel
@IsmaelMiguel 1: sí, <0queda la basura de la versión sin golf y debe eliminarse. 2: pensando de nuevo, el forpuede ser revisado para ser incluido en la parte repetida. Gracias de nuevo
edc65
5

Implementación de Python3 + PEG en 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())))

Hace un tiempo hice una implementación PEG en Python . Creo que puedo usar eso aquí.

Analiza la expresión en un árbol y solo guarda paréntesis si el hijo es una suma y el padre es una multiplicación.

orlp
fuente
4

Perl, 132 bytes

129 bytes fuente + 3 para -pbandera:

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

Utilizando:

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

Ruby, 140 130 bytes

127 bytes fuente + 3 para -pbandera:

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

Y sin golfos:

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 $_
ezrast
fuente
Muy buena respuesta. ¿Qué está pasando con la 0 whilesintaxis?
Jonás
1
@Jonah En Ruby, expr while condes equivalente a while cond; expr; end. Aquí, solo quiero realizar condrepetidamente y en realidad no tengo un cuerpo de bucle. Por lo general, uno escribiría esto como while cond; endo tal vez, loop{ break unless cond }pero 0 while condtiene menos bytes. El 0no hace nada; está ahí porque la forma corta del ciclo while requiere un cuerpo.
Ezrast
2

Retina, 155 bytes

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

Pruébalo en línea!

Verifique todas las cajas de prueba a la vez.

Explicación

Lo principal es este código:

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

Esta expresión regular puede coincidir con cualquier cadena en la que los corchetes estén equilibrados, por ejemplo, 1+(2+(3))+4o 2+3.

Para facilitar la explicación, deje que esta expresión regular sea B.

Además, usemos <y en su >lugar para los corchetes, así como py mpara \+y \*.

El código se convierte en:

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

Las dos primeras líneas coinciden para los corchetes que consisten en solo multiplicación, por ejemplo, (1*2*3)o incluso (1*(2+3)*4). Son reemplazados por su contenido en el interior.

Las dos últimas líneas coinciden con los corchetes que no están precedidos y que no van seguidos de multiplicación. Son reemplazados por su contenido en el interior.

La inicial {`significa "reemplazar hasta idempotente", lo que significa que los reemplazos se realizan hasta que ya no coinciden o se reemplazan con ellos mismos.

En este caso, los reemplazos se realizan hasta que ya no coinciden.

Monja permeable
fuente
Falla por 1*(2*(3+4)*5)*6.
orlp
@orlp Gracias, arreglado.
Leaky Nun
Falla para(1*(2+3)+4)*5
Sp3000 05 de
@ Sp3000 Gracias, arreglado.
Leaky Nun
2

Python 3, 274 269 359 337 336 bytes

Básicamente, este método elimina cada par de paréntesis y verificaciones posibles para ver si aún evalúa lo mismo.

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)

Arnés de prueba

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

Actualizaciones

  • -1 [16-10-04] Se eliminó el espacio extra
  • -22 [16-05-07] Hizo uso de la relib
  • +90 [16-05-07] Actualizado para manejar los nuevos casos de prueba
  • -5 [16-05-07] Se eliminó el parámetro de la longitud ( l) lambda
Fruta no lineal
fuente
1
Esto falla en el caso de prueba 1*(2+3), porque OP dijo que no se simplificara para casos de números especiales. Buena respuesta sin embargo; Esto tiene mi voto a favor.
HyperNeutrino
1
@AlexL. Gracias por atrapar eso! No actualicé mis casos de prueba D: Pero ahora está arreglado.
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);

No es una longitud impresionante, eso es lo que obtengo por adoptar un enfoque menos que óptimo (y usar un lenguaje menos que óptimo).

Elimina un par de corchetes, luego evalúa la expresión resultante. Si el resultado es el mismo que el original, lo agrega a un mapa de expresiones válidas y se repite hasta que no se puedan encontrar nuevas expresiones. Luego imprime la expresión válida más corta.

Se rompe cuando el resultado de la expresión se hace grande y aparece la notación de doble / exponente.

ToXik-yogurt
fuente
1

Prólogo (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.

Pruébalo en línea!

Define un predicado //2 que elimina los paréntesis del valor de la cadena de su primer argumento y genera una cadena a través de su segundo argumento. Si la entrada pudiera estar en términos de Prolog, esto sería solo 81 77 bytes definiendo +/2sin tener que lidiar con lo detalladoterm_string/2 , pero muchos paréntesis innecesarios simplemente no habrían existido para comenzar de esa manera, por lo que estaría muy cerca de hacer trampa, ya que todo lo que +/2hace es manejar la asociatividad.

Intenté usarlo =../2todo, pero salió mucho más tiempo, porque un operador de tres bytes que funciona con listas no es exactamente conciso:

Prólogo (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.

Pruébalo en línea!

Cadena no relacionada
fuente