Scrivi un programma che sostituisca con spazi le parentesi graffe nei casi in cui le parentesi graffe in alcuni punti causano stasi


17

Sei un project manager. Un giorno, uno dei tuoi programmatori è diventato pazzo ( non per colpa tua ) e ha preso tutte le espressioni nella base di codice e ha aggiunto parentesi casuali a loro, prima di abbandonare sul posto, lamentandosi della tua incompetenza ( anche non colpa tua ). Questa sarebbe una soluzione semplice, tuttavia, per qualche motivo non stai usando il controllo di revisione ( totalmente non per colpa tua ). E per qualche ragione, nessuno degli altri programmatori vuole esaminare ogni espressione per correggere le parentesi non corrispondenti ( a proposito, non è colpa tua ). Programmatori in questi giorni, pensi a te stesso. Dovrai farlo da solo. L'orrore! Tali compiti dovevano essere sotto di te ...

L'input sarà una riga singola, che conterrà un numero di parentesi quadre ( ( [ {) e parentesi quadre ( ) ] }). Può anche contenere, ma non sempre, commenti ( /* */) e valori letterali di stringa ( " "o ' ') e vari numeri, lettere o simboli.

Ci sarà almeno una parentesi (al di fuori di un commento o stringa letterale) che non ha un opposto corrispondente (al di fuori di un commento o stringa letterale). Ad esempio, un errante }senza un {precedente. Un altro esempio: un (che non ha un )dopo. Il tuo programma sostituirà con uno spazio il numero minimo di parentesi necessarie per far combaciare le parentesi.

Esempi:

(4 + (2 + 3))]==> (4 + (2 + 3)) (parentesi quadra alla fine)
][][[]]==> [][[]](parentesi quadra all'inizio)
("Hel(o!"))==> ("Hel(o!") (parentesi alla fine)
( /* )]*/==> /* )]*/ (parentesi all'inizio)
{()]==> () (parentesi graffa e la parentesi quadra)

  • L'input può essere preso nel modo più conveniente (STDIN, argomento della riga di comando, lettura da un file, ecc.)
  • Se esiste più di un modo per risolvere la mancata corrispondenza con lo stesso numero di rimozioni, entrambi sono accettabili.
  • Tra parentesi ci saranno solo discrepanze. I letterali e i commenti delle stringhe verranno sempre formati correttamente.
  • Il titolo deriva da questo thread SO
  • Non ci saranno mai citazioni nei commenti, citazioni tra virgolette, commenti nei commenti o commenti tra virgolette.

Questo è il codice golf, quindi vince il numero minimo di byte. Poni domande nei commenti se le specifiche non sono chiare.


Spiacenti, le nostre modifiche si sono scontrate lì. : P Ora tutto dovrebbe essere risolto.
Maniglia della porta

@Doorknob Grazie per quello, comunque. Non sapevo come fermare SE da pulire gli spazi.
assenzio

Dobbiamo gestire le cose in fuga nei letterali stringa (ad es. ("foo (\") bar"))?
Maniglia della porta

1
Direi che l'output corretto per {{(})dovrebbe essere { } o equivalente, poiché lo scenario di apertura implica che il codice stava funzionando all'inizio e {(})conta come parentesi non corrispondenti in ogni linguaggio di programmazione che conosco (cioè "causa stasi" ??). Ma poi ho già scritto una risposta, quindi sono di parte.
DLosc

3
Vedo. Immagino di non essere abbastanza incompetente. ;)
DLosc

Risposte:


6

Rubino, 223 caratteri

Questo si è rivelato un po 'lungo.

u,b,i=[],[[],[],[]],-1
s=gets.gsub(/(\/\*|"|').*?(\*\/|"|')|~/){|m|u+=[m];?~}
s.chars{|c|i+=1
(t='{[('.index(c))?b[t].push(i):((t='}])'.index(c))&&(b[t].pop||s[i]=' '))}
b.flatten.map{|l|s[l]=' '}
puts s.gsub(/~/){u.shift}

Ciò che fa è togliere prima le stringhe e i commenti, in modo che non vengano conteggiati (e li rimetta in un secondo momento).

Quindi, passa attraverso la stringa carattere per carattere. Quando trova una parentesi graffa di apertura, memorizza la sua posizione. Quando trova un controvento di chiusura, viene espulso dal rispettivo array di archiviazione del controvento aperto.

Se poprestituisce nil(ovvero non c'erano abbastanza parentesi graffe di apertura), rimuove la parentesi graffa di chiusura. Al termine dell'intera operazione, rimuove le parentesi graffe di apertura extra rimanenti (ovvero non c'erano abbastanza parentesi graffe di chiusura).

Alla fine del programma, rimette tutte le stringhe e i commenti e li restituisce.

Ungolfed:

in_str = gets

# grab strings and comments before doing the replacements
i, unparsed = 0, []
in_str.gsub!(/(\/\*|"|').*?(\*\/|"|')|\d/){|match| unparsed.push match; i += 1 }

# replaces with spaces the braces in cases where braces in places cause stasis
brace_locations = [[], [], []]
in_str.each_char.with_index do |chr, idx|
    if brace_type = '{[('.index(chr)
        brace_locations[brace_type].push idx
    elsif brace_type = '}])'.index(chr)
        if brace_locations[brace_type].length == 0
            in_str[idx] = ' '
        else
            brace_locations[brace_type].pop
        end
    end
end
brace_locations.flatten.each{|brace_location| in_str[brace_location] = ' ' }

# put the strings and comments back and print
in_str.gsub!(/\d+/){|num| unparsed[num.to_i - 1] }
puts in_str

Questo è davvero impressionante. Una domanda, però: funzionerà comunque per un input simile (("string"/*comment*/)"string"? Se sto leggendo correttamente (la versione non golfata), sostituisci stringhe e commenti con il loro indice unparsednell'array, il che porterebbe a una sostituzione come ((12)3e quindi alla ricerca di un indice inesistente 12(o 11). Vedo solo la versione golfata shift, ma potrebbe non avere ancora un problema simile?
DLosc

4

Python 3, 410 322 317

import re;a='([{';z=')]}';q=[re.findall('".*?"|/\*.*?\*/|.',input())]
while q:
 t=q.pop(0);s=[];i=0
 for x in t:
  if x in a:s+=[x]
  try:x in z and 1/(a[z.find(x)]==s.pop())
  except:s=0;break
 if[]==s:print(''.join(t));break
 while 1:
  try:
   while t[i]not in a+z:i+=1
  except:break
  u=t[:];u[i]=' ';q+=[u];i+=1

Prova ogni possibile serie di eliminazioni, iniziando da quelle più piccole, fino a quando non trova quella in cui le parentesi graffe sono bilanciate. (Con questo intendo perfettamente bilanciato correttamente: {{(})produce ( ), non {(}).)

La prima versione utilizzava una funzione di generatore ricorsivo, che era davvero interessante ma anche molto lunga. Questa versione esegue una semplice ricerca in ampiezza usando una coda. (Sì, è un algoritmo temporale fattoriale. Qual è il problema?: ^ D)


Mi piace questo perché trova effettivamente la minima rimozione e produce espressioni nidificate correttamente, ma l'ultimo commento di @vonilya suggerisce che l'annidamento corretto non è importante. Tuttavia, è molto lento se è necessario rimuovere molte parentesi graffe.
rici,

2

C - 406

Un tentativo in C senza usare espressioni regolari.

#define A if((d==125||d==93||d==41)
char*s;t[256];f(i,m,n,p){while(s[i]!=0){int c=s[i],k=s[i+1],v=1,d;if((c==42&&k==47)||(c==m&&i>n))return i;if(!p||p==2){if((c==39||c==34)||(c==47&&k==42)){i=f(i,c*(c!=47),i,p+1);
c=c==47?42:c;}d=c+1+1*(c>50);A){v=f(i+1,d,i,2);if(!p&&v)t[d]++;if(p==2&&v)i=v;}}d=c;A&&!p){v=!!t[c];t[c]-=v;}if(p<2)putchar(c*!!v+32*!v);i++;}return 0;}main(int c,char*v[]){s=v[1];f(0,0,0,0);}

Per compilare ed eseguire (su una macchina linux):
gcc -o brackets brackets.c
./brackets "[(])"

In casi non definiti come [(]) restituisce l'ultima coppia di parentesi quadre valida ()


2

Python 3, 320

import re
O=dict(zip('([{',')]}'))
def o(s,r,m=0,t=[]):m+=re.match(r'([^][)({}/"]|/(?!\*)|/\*.*?\*/|".*?")*',s[m:]).end();return r and o(s[:m]+' '+s[m+1:],r-1,m+1,t)or(o(s,r,m+1,t+[O[s[m]]])if s[m]in O else[s[m]]==t[-1:]and o(s,r,m+1,t[:-1]))if s[m:]else not t and s
s=input();i=0;a=0
while not a:a=o(s,i);i+=1
print(a)

Come la soluzione DLosc, questo indaga su ogni possibile eliminazione, ma utilizza una strategia ricorsiva di esplorazione e fallback che è molto più veloce. So che la velocità non è un criterio nel code golf, e la ricerca esaustiva è in ogni caso esponenziale, ma questa può gestire input come ({({({({({({({({(}}}}}}}}in un paio di secondi.


Ben giocato, ben giocato. Sono riuscito a scendere a 317, ma penso che dovresti riuscire a passarlo abbastanza facilmente. (Nel frattempo, il mio programma sta ancora sfornando il tuo esempio di input ...)
DLosc

@DLosc: non trattenere il respiro :). La mia macchina ha impiegato 58 minuti per eseguire la versione di quel modello con 6 parentesi aperte. Per risolvere la stasi prima che l'universo raggiunga la morte per calore, dovrai memorizzare la coda; altrimenti, si finisce con una O(n!!)soluzione, no O(n!). (Il mio golf O(n*2^n)invece è O(2^n), perché in orealtà produce tutti i modelli con fino alle rrimozioni, invece di esatte rrimozioni. Facile da riparare, ma costerebbe alcuni caratteri.)
rici
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.