Escriba un programa que reemplace con espacios las llaves en los casos en que las llaves en lugares causen estasis

17

Eres un gerente de proyecto. Un día, uno de sus programadores se volvió loco ( no es su culpa ) y tomó todas las expresiones en la base de código y agregó corchetes al azar, antes de abandonar en el acto, despotricando sobre su incompetencia ( tampoco es su culpa ). Sin embargo, esta sería una solución fácil, por alguna razón no está utilizando el control de revisión ( no es su culpa ). Y por alguna razón, ninguno de los otros programadores quiere pasar por cada expresión para arreglar los corchetes no coincidentes ( por cierto, eso no es tu culpa ). Programadores en estos días, piensas para ti mismo. Tendrás que hacerlo tú mismo. ¡El horror! Se suponía que tales tareas estarían debajo de ti ...

La entrada será una sola línea, que contendrá una serie de corchetes izquierdos ( ( [ {) y corchetes derechos ( ) ] }). También puede, pero no siempre, contener comentarios ( /* */) y literales de cadena ( " "o ' ') y varios números, letras o símbolos.

Habrá al menos un paréntesis (fuera de un comentario o literal de cadena) que no tenga un opuesto de corrosión (fuera de un comentario o literal de cadena). Por ejemplo, un errante }sin un {previo. Otro ejemplo: un (que no tiene un )después. Su programa reemplazará con un espacio el número mínimo de paréntesis necesarios para que coincidan los paréntesis.

Ejemplos:

(4 + (2 + 3))]==> (4 + (2 + 3)) (el corchete al final)
][][[]]==> [][[]](el corchete al comienzo)
("Hel(o!"))==> ("Hel(o!") (el paréntesis al final)
( /* )]*/==> /* )]*/ (el paréntesis al comienzo)
{()]==> () (el corchete y el corchete)

  • La entrada se puede tomar de la forma más conveniente (STDIN, argumento de línea de comando, lectura de un archivo, etc.)
  • Si hay más de una forma de resolver la falta de coincidencia con el mismo número de eliminaciones, es aceptable.
  • Solo habrá desajustes entre paréntesis. Los literales de cadena y los comentarios siempre se formarán correctamente.
  • El título proviene de este hilo SO
  • Nunca habrá citas en comentarios, citas en citas, comentarios en comentarios o comentarios en citas.

Este es el código de golf, por lo que gana el número mínimo de bytes. Haga preguntas en los comentarios si la especificación no es clara.

Ajenjo
fuente
Vaya, nuestras ediciones chocaron allí. : P Todo debería estar arreglado ahora.
Pomo de la puerta
@Doorknob Gracias por eso, por cierto. No sabía cómo detener SE de limpiar los espacios.
absenta
¿Tenemos que manejar cosas de escape en literales de cadena (por ejemplo ("foo (\") bar"))?
Pomo de la puerta
1
Yo diría que la salida correcta para {{(})debería ser { } o equivalente, ya que el escenario de apertura implica que el código estaba funcionando para empezar, y {(})cuenta como paréntesis no coincidentes en cada lenguaje de programación que conozco (es decir, "causa estasis"). Pero, entonces, ya escribí una respuesta, así que soy parcial.
DLosc
3
Veo. Supongo que no soy lo suficientemente incompetente. ;)
DLosc

Respuestas:

6

Ruby, 223 caracteres

Este resultó un poco largo.

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}

Lo que hace es sacar primero las cadenas y los comentarios, para que no se cuenten (y los vuelve a colocar más tarde).

Luego, pasa por la cadena carácter por carácter. Cuando encuentra una llave de apertura, almacena su posición. Cuando encuentra una llave de cierre, emerge de su respectiva matriz de almacenamiento de llaves abiertas.

Si popregresa nil(es decir, no había suficientes llaves de apertura), elimina la llave de cierre. Una vez hecho todo esto, elimina las llaves de apertura adicionales restantes (es decir, no había suficientes llaves de cierre).

Al final del programa, vuelve a colocar todas las cadenas y comentarios y los genera.

Sin golf:

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
Pomo de la puerta
fuente
Esto es realmente impresionante. Sin embargo, una pregunta: ¿seguirá funcionando para una entrada como (("string"/*comment*/)"string"? Si estoy leyendo (la versión no modificada) correctamente, reemplaza cadenas y comentarios con su índice en la unparsedmatriz, lo que llevaría a una sustitución como ((12)3y luego buscaría un índice inexistente 12(o 11). Veo que la versión de golf solo usa shift, pero ¿podría no tener un problema similar?
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

Intenta cada posible conjunto de eliminaciones, comenzando con las más pequeñas, hasta que encuentre una donde los frenos estén equilibrados. (Con lo cual quiero decir que está completamente equilibrado: {{(})produce ( ), no {(})).

La primera versión usaba una función de generador recursivo, que era realmente genial pero también muy larga. Esta versión realiza una búsqueda simple de amplitud utilizando una cola. (Sí, es un algoritmo de tiempo factorial. ¿Cuál es el problema?: ^ D)

DLosc
fuente
Me gusta este porque realmente encuentra la menor eliminación y produce expresiones correctamente anidadas, pero el último comentario de @vonilya sugiere que el anidamiento correcto no es importante. Sin embargo, es realmente lento si es necesario quitar muchos aparatos ortopédicos.
rici
2

C - 406

Un intento en C sin usar expresiones regulares.

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

Para compilar y ejecutar (en una máquina Linux):
gcc -o brackets brackets.c
./brackets "[(])"

En casos indefinidos como [(]) devuelve el último par de paréntesis válido ()

Markuz
fuente
2

Pitón 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)

Al igual que la solución de DLosc, esta investiga todas las eliminaciones posibles, pero utiliza una estrategia de exploración y recuperación recursiva que es mucho más rápida. Sé que la velocidad no es un criterio en el código de golf, y la búsqueda exhaustiva es en cualquier caso exponencial, pero esta puede manejar entradas como ({({({({({({({({(}}}}}}}}en un par de segundos.

rici
fuente
Bien jugado, bien jugado. Me las arreglé para llegar al 317, pero creo que deberías poder pasar eso con bastante facilidad. (Mientras tanto, mi programa todavía está produciendo su entrada de ejemplo ...)
DLosc
@DLosc: No contengas la respiración :). Mi máquina tardó 58 minutos en hacer la versión de ese patrón con 6 pares abiertos. Para resolver la estasis antes de que el universo alcance la muerte por calor, deberá memorizar la cola; de lo contrario, terminas con una O(n!!)solución, no O(n!). (Mi golf es en O(n*2^n)lugar de O(2^n), porque en orealidad produce todos los patrones con hasta reliminaciones, en lugar de reliminaciones exactas . Fácil de arreglar, pero costaría unos pocos caracteres.)
rici