Escreva um programa que substitua por espaços os aparelhos nos casos em que os aparelhos em lugares causam estase

17

Você é um gerente de projeto. Um dia, um de seus programadores enlouqueceu ( não foi sua culpa ) e pegou todas as expressões na base de código e adicionou colchetes aleatórios a eles, antes de sair no local, reclamando sobre sua incompetência ( também não é sua culpa ). Isso seria uma solução fácil, no entanto, por algum motivo, você não está usando o controle de revisão ( totalmente não é sua culpa ). E, por alguma razão, nenhum dos outros programadores deseja passar por todas as expressões para corrigir os colchetes incompatíveis ( a propósito, isso não é culpa sua ). Programadores hoje em dia, você pensa consigo mesmo. Você terá que fazer isso sozinho. O horror! Essas tarefas deveriam estar abaixo de você ...

A entrada será uma linha única, que conterá um número de colchetes esquerdo ( ( [ {) e colchetes direito ( ) ] }). Também pode, mas nem sempre, conter comentários ( /* */) e literais de sequência ( " "ou ' ') e vários números, letras ou símbolos.

Haverá pelo menos um colchete (fora de um comentário ou literal de cadeia) que não possui um oposto de corrosão (fora de um comentário ou literal de cadeia). Por exemplo, um errante }sem um {prior. Outro exemplo: um (que não possui um )depois. Seu programa substituirá por um espaço o número mínimo de colchetes necessário para fazer com que os colchetes correspondam.

Exemplos:

(4 + (2 + 3))]==> (4 + (2 + 3)) (o colchete no final)
][][[]]==> [][[]](o colchete no início)
("Hel(o!"))==> ("Hel(o!") (o parêntese no final)
( /* )]*/==> /* )]*/ (o parêntese no início)
{()]==> () (o colchete e o colchete)

  • A entrada pode ser obtida da maneira que for mais conveniente (STDIN, argumento da linha de comando, leitura de um arquivo etc.)
  • Se houver mais de uma maneira de resolver a incompatibilidade com o mesmo número de remoções, qualquer uma delas é aceitável.
  • Haverá apenas incompatibilidades entre colchetes. Literais e comentários de strings sempre serão formados corretamente.
  • O título vem deste tópico SO
  • Nunca haverá aspas nos comentários, aspas nas aspas, comentários nos comentários ou comentários nas aspas.

Este é o código golf, portanto, o número mínimo de bytes vence. Faça perguntas nos comentários se a especificação não estiver clara.

absinto
fonte
Opa, nossas edições meio que colidiram lá. : P Tudo deve ser corrigido agora.
Maçaneta
@ Doorknob Obrigado por isso, a propósito. Não sabia como parar SE de limpar os espaços.
o absinto
Temos que lidar com coisas de escape em literais de string (por exemplo ("foo (\") bar"))?
Maçaneta
11
Eu argumentaria que a saída correta para {{(})deveria ser { } ou equivalente, já que o cenário de abertura implica que o código estava trabalhando para começar e {(})conta como colchetes incompatíveis em todas as linguagens de programação que eu conheço (ou seja, "causa estase" ??). Mas, então, eu já escrevi uma resposta, então sou tendenciosa.
DLosc 21/08/14
3
Entendo. Acho que não sou incompetente o suficiente. ;)
DLosc 21/08

Respostas:

6

Ruby, 223 caracteres

Este acabou um pouco longo.

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}

O que ele faz é retirar as strings e os comentários primeiro, para que não sejam contados (e os devolva mais tarde).

Em seguida, ele passa pela string caractere por caractere. Quando encontra uma chave de abertura, armazena sua posição. Quando encontra uma chave de fechamento, ela aparece em sua respectiva matriz de armazenamento de chave aberta.

Se popretornar nil(ou seja, não havia chaves de abertura suficientes), ela remove a chave de fechamento. Depois que tudo estiver pronto, ele remove os demais chaves de abertura extras (ou seja, não havia chaves de fechamento suficientes).

No final do programa, ele coloca todas as strings e comentários de volta e as produz.

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
Maçaneta da porta
fonte
Isto é seriamente impressionante. Uma pergunta, no entanto: ainda funcionará para uma entrada como (("string"/*comment*/)"string"? Se estou lendo (a versão não-gasta) corretamente, você substitui as strings e comentários pelo respectivo índice na unparsedmatriz, o que levaria a uma substituição como ((12)3e, em seguida, procurando por um índice inexistente 12(ou 11). Vejo que a versão golfada apenas usa shift, mas ela ainda não pode ter um problema semelhante?
DLosc 21/08/14
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

Tenta todos os conjuntos de exclusões possíveis, começando pelos menores, até encontrar um onde os chavetas estejam equilibrados. (Com o que quero dizer totalmente corretamente equilibrado: {{(})produz ( ), não {(}).)

A primeira versão usou uma função de gerador recursivo, que foi muito legal, mas também muito longa. Esta versão realiza uma pesquisa simples pela primeira vez usando uma fila. (Sim, é um algoritmo fatorial de tempo. Qual é o problema?: ^ D)

DLosc
fonte
Eu gosto deste porque ele realmente encontra a menor remoção e produz expressões aninhadas corretamente, mas o último comentário de @vonilya sugere que o aninhamento correto não é importante. No entanto, é muito lento se for necessário remover várias chaves.
rici 21/08
2

C - 406

Uma tentativa em C sem usar expressões 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 e executar (em uma máquina Linux):
gcc -o brackets brackets.c
./brackets "[(])"

Em casos indefinidos como [(]), ele retorna o último par de colchetes válido ()

Markuz
fonte
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)

Como a solução do DLosc, isso investiga todas as exclusões possíveis, mas usa uma estratégia recursiva de exploração e fallback que é muito mais rápida. Eu sei que a velocidade não é um critério no código de golfe e, de qualquer forma, a pesquisa exaustiva é exponencial, mas essa pode lidar com entradas como ({({({({({({({({(}}}}}}}}em alguns segundos.

rici
fonte
Bem jogado, bem jogado. Consegui chegar ao 317, mas acho que você deve conseguir passar com facilidade. (Enquanto isso, o meu programa ainda está produzindo afastado em seu exemplo de entrada ...)
DLosc
@DLosc: Não prenda a respiração :). Levou minha máquina 58 minutos para fazer a versão desse padrão com 6 parênteses abertos. Para resolver a estase antes que o universo atinja a morte por calor, você precisará memorizar a fila; caso contrário, você terá uma O(n!!)solução, não O(n!). (My golfe é O(n*2^n), em vez de O(2^n), porque o, na verdade, produz todos os padrões com até rremoções, em vez de exatamente rremoções fácil de corrigir, mas que custa alguns personagens..)
rici