Напишите программу, которая заменяет пробелы скобками в тех случаях, когда скобки в местах вызывают застой

17

Вы менеджер проекта. Однажды один из ваших программистов сошел с ума ( не по вашей вине ) и взял все выражения в кодовой базе и добавил к ним случайные скобки, прежде чем выйти на месте, разглагольствуя о вашей некомпетентности ( также не по вашей вине ). Это было бы легко исправить, однако, по какой-то причине вы не используете контроль версий ( совсем не ваша вина ). И по какой-то причине никто из других программистов не хочет просматривать каждое выражение, чтобы исправить несоответствующие скобки ( кстати, это не ваша ошибка ). Программисты в эти дни думают про себя. Вам придется сделать это самостоятельно. Ужас! Такие задачи должны были быть под вами ...

Ввод будет состоять из одной строки, которая будет содержать несколько левых скобок ( ( [ {) и правых скобок ( ) ] }). Он также может, но не всегда, содержать комментарии ( /* */) и строковые литералы ( " "или ' '), а также различные числа, буквы или символы.

Будет по крайней мере одна скобка (вне комментария или строкового литерала), которая не имеет противоположной противоположности (вне комментария или строкового литерала). Например, странствующий }без {предварительного. Другой пример: у (которого нет )потом. Ваша программа заменит пробелом минимальное количество скобок, необходимое для совпадения скобок.

Примеры:

(4 + (2 + 3))]==> (4 + (2 + 3)) (квадратная скобка в конце)
][][[]]==> [][[]](квадратная скобка в начале)
("Hel(o!"))==> ("Hel(o!") (скобка в конце)
( /* )]*/==> /* )]*/ (скобка в начале)
{()]==> () (фигурная скобка и квадратная скобка)

  • Ввод может быть получен любым удобным способом (STDIN, аргумент командной строки, чтение из файла и т. Д.)
  • Если существует несколько способов устранения несоответствия с одинаковым количеством удалений, любой из них является приемлемым.
  • В скобках будут только несоответствия. Строковые литералы и комментарии всегда будут правильно сформированы.
  • Название происходит из этой темы
  • Никогда не будет никаких кавычек в комментариях, кавычек в кавычках, комментариев в комментариях или комментариев в кавычках.

Это код гольф, поэтому выигрывает минимальное количество байтов. Задавайте вопросы в комментариях, если спецификация не ясна.

абсент
источник
К сожалению, наши правки вроде как столкнулись там. : P Все должно быть исправлено сейчас.
Ручка двери
@Doorknob Спасибо за это, кстати. Не знаю , как остановить SE от протирания пространства.
абсент
Должны ли мы обрабатывать экранирование в строковых литералах (например ("foo (\") bar"))?
Дверная ручка
1
Я бы сказал, что правильный вывод для {{(})должен быть { } или эквивалентен, так как сценарий открытия подразумевает, что код работал с самого начала, и {(})считается как несовпадающие скобки в каждом языке программирования, который я знаю (то есть «вызывает стазис» ??). Но тогда я уже написал ответ, поэтому я пристрастен.
DLosc
3
Понимаю. Думаю, я просто недостаточно компетентен. ;)
DLosc

Ответы:

6

Рубин, 223 символа

Этот оказался немного длинным.

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}

Сначала он извлекает строки и комментарии, чтобы они не учитывались (и возвращал их позже).

Затем он проходит через строку символ за символом. Когда он находит открывающую скобку, он сохраняет свою позицию. Когда он находит закрывающую фигурную скобку, он появляется из соответствующего массива хранения открытой фигурной скобки.

Если popвозвращается nil(т. Е. Не было достаточно открывающих скобок), он удаляет закрывающую скобку. После того, как все это сделано, удаляются оставшиеся дополнительные открывающие скобки (то есть не хватает закрывающих скобок).

В конце программы он возвращает все строки и комментарии обратно и выводит их.

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
Дверная ручка
источник
Это серьезно впечатляет. Один вопрос, тем не менее: он все еще будет работать для ввода, как (("string"/*comment*/)"string"? Если я правильно читаю (версия без заглавных букв), вы заменяете строки и комментарии их индексами в unparsedмассиве, что приводит к подстановке, ((12)3а затем ищет несуществующий индекс 12(или 11). Я вижу, что гольф-версия просто использует shift, но не может ли она все еще иметь подобную проблему?
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

Пытается выполнить все возможные наборы удалений, начиная с более мелких, до тех пор, пока не найдет тот, в котором скобки сбалансированы. (Под этим я подразумеваю полностью правильно сбалансированный: {{(})производит ( ), а не {(}).)

Первая версия использовала функцию рекурсивного генератора, которая была действительно крутой, но и очень длинной. Эта версия выполняет простой поиск в ширину с использованием очереди. (Да, это алгоритм факторного времени. В чем проблема?: ^ D)

DLosc
источник
Мне нравится этот, потому что он на самом деле находит наименьшее удаление и производит правильно вложенные выражения, но последний комментарий @vonilya предполагает, что правильное вложение не важно. Тем не менее, это очень медленно, если нужно удалить много скобок.
Ричи
2

С - 406

Попытка в C без использования регулярных выражений.

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

Чтобы скомпилировать и запустить (на компьютере с Linux):
gcc -o brackets brackets.c
./brackets "[(])"

В неопределенных случаях, таких как [(]), он возвращает последнюю действительную пару скобок ()

Markuz
источник
2

Питон 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)

Как и решение DLosc, в нем исследуются все возможные удаления, но в нем используется рекурсивная стратегия исследования и восстановления, которая намного быстрее. Я знаю, что скорость не является критерием в кодовом гольфе, и исчерпывающий поиск в любом случае экспоненциальный, но этот может обрабатывать входные данные, как ({({({({({({({({(}}}}}}}}за пару секунд.

RICi
источник
Хорошо сыграно, хорошо сыграно. Мне удалось спуститься до 317, но я думаю, что вы сможете пройти это довольно легко. (Тем временем моя программа все еще работает на вашем примере ввода ...)
DLosc
@DLosc: не задерживай дыхание :). Моей машине понадобилось 58 минут, чтобы сделать версию этого паттерна с 6 открытыми паренами. Для того чтобы решить стазис до того, как вселенная достигнет тепловой смерти, вам нужно запомнить очередь; в противном случае вы получите O(n!!)решение, а не O(n!). (Мой гольф O(n*2^n)вместо того O(2^n), потому что на oсамом деле он производит все модели вплоть до rудаления, а не просто rудаления. Легко исправить, но это будет стоить несколько символов.)
Ричи