Supprimer les parenthèses inutiles


32

Vous obtenez une chaîne composée des caractères 0123456789+*(). Vous pouvez supposer que la chaîne est toujours une expression mathématique valide.

Votre tâche consiste à supprimer les parenthèses inutiles, en supposant que la multiplication a une priorité plus élevée que l'addition.

Les parenthèses ne doivent être supprimées que lorsqu'elles ne sont pas nécessaires structurellement :

  • en raison de la multiplication de priorité plus élevée: 3+(4*5)=>3+4*5
  • en raison de la multiplication ou de l'associativité par addition: 3*(4*5)=>3*4*5
  • lorsqu'ils sont redondants autour d'une expression: 3*((4+5))=>3*(4+5)

Les parenthèses doivent être conservées lorsqu'elles peuvent être simplifiées en raison de valeurs numériques spécifiques:

  • 1*(2+3) ne devrait pas être simplifié 1*2+3
  • 0*(1+0) ne devrait pas être simplifié 0*1+0

Exemples:

(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
Plus de tests, s'il vous plaît?
Leaky Nun

2
1*(2*(3+4)*5)*6devrait être un testcase intéressant (pour lequel ma solution échoue actuellement).
Leaky Nun

8
Est-ce que «inutile» est défini structurellement ou au cas par cas? En d'autres termes, les parenthèses sont-elles inutiles ici? (2+2)*1
Luis Mendo

2
@LuisMendo Je pense qu'il est juste de l'interpréter dans les deux sens
anatolyg

2
@anatolyg Je ne pense pas que ce serait juste, car les approches pour les deux seraient très différentes. Ce serait bien si nous obtenions des éclaircissements.
Sp3000

Réponses:


15

Mathematica, 105 97 91 octets

-6 octets grâce à Roman !

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

Remplace +et *par ~~( StringExpression) et **( NonCommutativeMultiply) respectivement, l'évalue, le stringifie et remplace les opérateurs.


Quelle? Mathematica n'a pas de fonction intégrée?
Erik the Outgolfer

@EriktheGolfer C'est fondamentalement le cas; J'essaie de ne pas évaluer les opérateurs.
LegionMammal978

C'est pourquoi Mathematica est si annoncé et si cher ... à cause des incorporations je pense. Mais Mathematica n'a pas changé par rapport aux autres langues si le casse-tête est assez difficile, mais les «autres langues» ne rivalisent pas du tout ici.
Erik the Outgolfer du

91 octets en utilisant StringExpressionau lieu de Dot, et en supprimant la " "->""clause:a=StringReplace;ToString@ToExpression@a[#,{"*"->"**","+"->"~~"}]~a~{" ** "->"*","~~"->"+"}&
Roman

@Roman Merci! Il semble que vous ayez trouvé un autre bon opérateur non évalué associatif non commutatif qui ne se combine pas avec les nombres.
LegionMammal978

7

JavaScript (ES6) 163 178

Modifier 15 octets enregistrés 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`)

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

Tester

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>


Pourquoi avez-vous écrit y.indexOf('+')au lieu de y.indexOf`+`[...]? ([...] ajouté pour éviter de trébucher sur le formatage) Était-ce un bug de cette façon?
Ismael Miguel

1
Voilà, 170 octets: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 c'est vraiment intelligent, merci! Leçon apprise: lors du passage à eval,
repensez

Je suis content que vous ayez aimé ma solution simple pour réduire votre code. J'aimerais pouvoir faire quelque chose for(b=, =='*'et d'autres bits répétés. Aussi, n'est-ce pas ~y.indexOf('+')<0la même chose que ~y.indexOf('+')? Étant donné que la seule valeur indexOf()renvoyée qui s'évalue à une valeur falsifiée est -1, la <0semble redondante. Ou, si je me trompe, vous pourriez le fairey.indexOf('+')>1
Ismael Miguel

@IsmaelMiguel 1: oui, la <0merde reste de la version non golfée et doit être supprimée. 2: en y repensant, le forpeut être révisé pour être inclus dans la partie répétée. Merci encore
edc65

5

Implémentation Python3 + PEG en Python , 271 octets

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

Il y a quelque temps, j'ai fait une implémentation PEG en Python . Je suppose que je peux l'utiliser ici.

Analyse l'expression dans un arbre et ne conserve les parenthèses que si l'enfant est l'addition et le parent la multiplication.


4

Perl, 132 octets

129 octets source + 3 pour l' -pindicateur:

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

En utilisant:

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

4

Rubis, 140 130 octets

127 octets source + 3 pour l' -pindicateur:

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

Et non golfé:

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

très belle réponse. que se passe-t-il avec la 0 whilesyntaxe?
Jonah

1
@Jonah In Ruby, expr while condest équivalent à while cond; expr; end. Ici, je veux seulement jouer condplusieurs fois et je n'ai pas de corps de boucle. Habituellement, on écrirait cela comme while cond; endou peut-être, loop{ break unless cond }mais il 0 while condy a moins d'octets. Le 0ne fait rien; c'est juste parce que la forme courte de la boucle while nécessite un corps.
ezrast

2

Rétine, 155 octets

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

Essayez-le en ligne!

Vérifiez tous les cas de test à la fois.

Explication

L'essentiel est ce code:

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

Cette expression régulière peut correspondre à n'importe quelle chaîne dans laquelle les crochets sont équilibrés, par exemple 1+(2+(3))+4ou 2+3.

Pour la facilité d'explication, que ce regex soit B.

Utilisons également <et à la >place pour les crochets, ainsi que pet mpour \+et \*.

Le code devient:

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

Les deux premières lignes correspondent aux parenthèses qui consistent uniquement en une multiplication, par exemple (1*2*3)ou même (1*(2+3)*4). Ils sont remplacés par leur contenu à l'intérieur.

Les deux dernières lignes correspondent aux parenthèses qui ne sont pas précédées et qui ne sont pas suivies de multiplication. Ils sont remplacés par leur contenu à l'intérieur.

Le premier {`signifie "remplacer jusqu'à idempotent", ce qui signifie que les remplacements sont effectués jusqu'à ce qu'ils ne correspondent plus ou qu'ils soient remplacés par eux-mêmes.

Dans ce cas, les remplacements sont effectués jusqu'à ce qu'ils ne correspondent plus.


Échoue pour 1*(2*(3+4)*5)*6.
orlp

@orlp Merci, corrigé.
Leaky Nun

Échoue pour(1*(2+3)+4)*5
Sp3000

@ Sp3000 Merci, corrigé.
Leaky Nun

2

Python 3, 274 269 359 337 336 octets

Cette méthode supprime essentiellement toutes les paires de parenthèses possibles et vérifie si elle est toujours la même.

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)

Harnais de test

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

Mises à jour

  • -1 [16-10-04] Espace supplémentaire supprimé
  • -22 [16-05-07] Utilisation de la relib
  • +90 [16-05-07] Mis à jour pour gérer les nouveaux cas de test
  • -5 [16-05-07] Paramètre supprimé de la longueur ( l) lambda

1
Cela échoue au cas de test 1*(2+3), car OP a dit de ne pas simplifier pour les cas de numéros spéciaux. Bonne réponse cependant; cela a mon upvote.
HyperNeutrino

1
@AlexL. Merci d'avoir attrapé ça! Je n'ai pas mis à jour mes cas de test D: Mais c'est corrigé maintenant.
NonlinearFruit

1

PHP, 358 octets

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

Pas une longueur impressionnante, c'est ce que j'obtiens en adoptant une approche moins qu'optimale (et en utilisant un langage moins qu'optimal).

Supprime une paire de crochets, puis met à jour l'expression résultante. Si le résultat est le même que l'original, il l'ajoute à une carte d'expressions valides et se reproduit jusqu'à ce qu'aucune nouvelle expression ne soit trouvée. Imprime ensuite l'expression valide la plus courte.

Arrête lorsque le résultat de l'expression devient volumineux et s'affiche en notation double / exposant.


1

Prolog (SWI) , 122 118 octets

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.

Essayez-le en ligne!

Définit un prédicat //2qui supprime les parenthèses de la valeur de chaîne de son premier argument et génère une chaîne via son deuxième argument. Si l'entrée pouvait être en termes Prolog, cela ne serait que de 81 77 octets définissant +/2sans avoir à traiter avec le verbeux term_string/2, mais beaucoup de parenthèses inutiles n'auraient tout simplement pas existé pour commencer de cette façon, donc ce serait assez proche de la tricherie, car il +/2ne s'agit que de gérer l'associativité.

J'ai essayé de l'utiliser =../2pour tout cela, mais cela est sorti beaucoup plus longtemps, car un opérateur à trois octets qui fonctionne avec des listes n'est pas exactement concis:

Prolog (SWI) , 124 octets

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.

Essayez-le en ligne!

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.