Rimuovere le parentesi non necessarie


32

Ti viene data una stringa composta con i caratteri 0123456789+*(). Puoi presumere che la stringa sia sempre un'espressione matematica valida.

Il tuo compito è rimuovere le parentesi non necessarie, supponendo che la moltiplicazione abbia una priorità maggiore rispetto all'aggiunta.

Le parentesi devono essere rimosse solo quando non sono necessarie strutturalmente :

  • a causa della moltiplicazione priorità più alta: 3+(4*5)=>3+4*5
  • a causa della moltiplicazione o dell'associatività dell'addizione: 3*(4*5)=>3*4*5
  • quando sono ridondanti attorno a un'espressione: 3*((4+5))=>3*(4+5)

Le parentesi dovrebbero essere mantenute quando potrebbero essere semplificate a causa di valori numerici specifici:

  • 1*(2+3) non dovrebbe essere semplificato a 1*2+3
  • 0*(1+0) non dovrebbe essere semplificato a 0*1+0

Esempi:

(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
Altre prove per favore?
Leaky Nun,

2
1*(2*(3+4)*5)*6dovrebbe essere un testcase interessante (per il quale la mia soluzione attualmente non riesce).
Leaky Nun,

8
"Non necessario" è definito strutturalmente o caso per caso? In altre parole, le parentesi non sono necessarie qui? (2+2)*1
Luis Mendo,

2
@LuisMendo Penso che sia giusto interpretarlo in entrambi i modi
anatolyg

2
@anatolyg Non penso che sarebbe giusto, perché gli approcci per i due sarebbero molto diversi. Sarebbe bello se avessimo dei chiarimenti.
Sp3000,

Risposte:


15

Mathematica, 105 97 91 byte

-6 byte grazie a Roman !

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

Sostituisce +e rispettivamente *con ~~( StringExpression) e **( NonCommutativeMultiply), lo valuta, lo stringe e sostituisce gli operatori.


Che cosa? Mathematica non ha un built-in?
Erik the Outgolfer,

@EriktheGolfer In pratica lo fa; Sto cercando di farlo non valutare gli operatori.
LegionMammal978,

Ecco perché Mathematica è così pubblicizzata e così costosa ... a causa dei built-in che penso. Ma Mathematica non ha avuto un cambiamento rispetto ad altre lingue se il puzzle è abbastanza difficile, eppure "altre lingue" non competono affatto qui.
Erik the Outgolfer,

91 byte utilizzando StringExpressioninvece di Dote rimuovendo la " "->""clausola:a=StringReplace;ToString@ToExpression@a[#,{"*"->"**","+"->"~~"}]~a~{" ** "->"*","~~"->"+"}&
Roman

@Roman Thanks! Sembra che tu abbia trovato un altro buon operatore associativo non commutativo non valutato che non si combina con i numeri.
LegionMammal978,

7

JavaScript (ES6) 163 178

Modifica 15 byte salvati grazie 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`)

Meno 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
}

Test

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>


Perché hai scritto y.indexOf('+')invece di y.indexOf`+`[...]? ([...] aggiunto per evitare di inciampare nella formattazione) Stava andando avanti in quel modo?
Ismael Miguel,

1
Ecco qua, 170 byte: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 è davvero intelligente, grazie! Lezione appresa: quando passi ad eval,
ripensaci

Sono contento che ti sia piaciuta la mia semplice soluzione per ridurre il tuo codice. Vorrei poter fare qualcosa for(b=, =='*'e altri bit ripetuti. Inoltre, non è ~y.indexOf('+')<0lo stesso di ~y.indexOf('+')? Poiché l'unico valore che indexOf()restituisce che valuta un valore errato è -1, <0sembra ridondante. Oppure, se ho sbagliato, potresti farloy.indexOf('+')>1
Ismael Miguel,

@IsmaelMiguel 1: sì, <0è la merda rimasta dalla versione non giocata e dovrebbe essere rimossa. 2: ripensandoci, è forpossibile rivederlo per includerlo nella parte ripetuta. Grazie ancora
edc65

5

Python3 + Implementazione PEG in Python , 271 byte

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

Qualche tempo fa ho realizzato un'implementazione PEG in Python . Immagino di poterlo usare qui.

Analizza l'espressione in un albero e mantiene la parentesi solo se il figlio è un'aggiunta e il padre è la moltiplicazione.


4

Perl, 132 byte

129 byte sorgente + 3 per -pflag:

#!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

Rubino, 140 130 byte

127 byte sorgente + 3 per -pflag:

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

E non golfato:

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

risposta molto bella. cosa sta succedendo con la 0 whilesintassi?
Giona il

1
@Jonah In Ruby, expr while condequivale a while cond; expr; end. Qui, voglio solo esibirmi condripetutamente e in realtà non ho un body loop. Di solito si scrive questo come while cond; endo forse loop{ break unless cond }ma 0 while condè meno byte. Il 0non fare nulla; è proprio lì perché la forma abbreviata del ciclo while richiede un corpo.
ezrast,

2

Retina, 155 byte

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

Provalo online!

Verifica tutti i test in una volta.

Spiegazione

La cosa principale è questo codice:

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

Questa espressione regolare può adattarsi a qualsiasi stringa in cui sono bilanciati le staffe, ad esempio 1+(2+(3))+4o 2+3.

Per la facilità di spiegazione, lascia che sia questa regex B.

Inoltre, usiamo <e >invece per le parentesi, così come pe mper \+e \*.

Il codice diventa:

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

Le prime due righe corrispondono alle parentesi che consistono solo nella moltiplicazione, ad esempio (1*2*3)o pari (1*(2+3)*4). Sono sostituiti dal loro contenuto all'interno.

Le ultime due righe corrispondono alle parentesi che non sono precedute e che non sono seguite dalla moltiplicazione. Sono sostituiti dal loro contenuto all'interno.

L'iniziale {`significa "sostituisci fino a idempotente", nel senso che le sostituzioni vengono eseguite fino a quando non corrispondono più o vengono sostituite con se stesse.

In questo caso, le sostituzioni vengono eseguite fino a quando non corrispondono più.


Non funziona per 1*(2*(3+4)*5)*6.
orlp,

@orlp Grazie, risolto.
Leaky Nun,

Non riesce per(1*(2+3)+4)*5
Sp3000,

@ Sp3000 Grazie, risolto.
Leaky Nun,

2

Python 3, 274 269 359 337 336 byte

Questo metodo sostanzialmente rimuove ogni possibile coppia di parentesi e controlla se valuta ancora lo stesso.

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)

Collaudare l'imbragatura

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

aggiornamenti

  • -1 [16-10-04] Spazio extra rimosso
  • -22 [16-05-07] Ha fatto uso della relib
  • +90 [16-05-07] Aggiornato per gestire i nuovi casi di test
  • -5 [16-05-07] Rimosso parametro dalla lunghezza ( l) lambda

1
Questo fallisce il caso di test 1*(2+3), perché OP ha detto di non semplificare per casi di numeri speciali. Buona risposta però; questo ha il mio voto.
HyperNeutrino

1
@AlexL. Grazie per averlo catturato! Non ho aggiornato i miei casi di test D: Ma ora è stato risolto.
NonlinearFruit

1

PHP, 358 byte

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

Non è una lunghezza impressionante, questo è quello che ottengo adottando un approccio meno che ottimale (e usando un linguaggio meno che ottimale).

Elimina una coppia di parentesi, quindi elimina l'espressione risultante. Se il risultato è uguale all'originale, lo aggiunge a una mappa di espressioni e ricorsi validi fino a quando non è possibile trovare nuove espressioni. Quindi stampa l'espressione valida più breve.

Si interrompe quando il risultato dell'espressione diventa grande e vengono visualizzati i notazioni doppio / esponente.


1

Prolog (SWI) , 122 118 byte

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.

Provalo online!

Definisce un predicato //2che rimuove le parentesi dal valore di stringa del suo primo argomento e genera una stringa attraverso il suo secondo argomento. Se l'input potesse essere in termini di Prolog, questo sarebbe solo 81 77 byte che definiscono +/2senza avere a che fare con il verboso term_string/2, ma molte parentesi non necessarie semplicemente non esisterebbero in quel modo, quindi sarebbe abbastanza vicino a barare, dal momento che tutto ciò che +/2fa è gestire l'associatività.

Ho provato a usarlo =../2per tutto, ma è uscito molto più a lungo, perché un operatore a tre byte che funziona con le liste non è esattamente conciso:

Prolog (SWI) , 124 byte

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.

Provalo online!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.