JavaScript (ES7), 121 117 байт
x=>(a=b=0,[for(c of x)for(d of'1234')(e=c.charCodeAt()/26|0)==d?a^=1<<d:b^=(a>>d&1)<<d*4+e],f=y=>y&&y%2+f(y>>1))(b)/2
Вау. Это было весело. Я набросал идею ответа, когда впервые появился этот вызов, но его длина превышала 150 байтов, и я не хотел прикладывать усилия для его игры в гольф. Вчера я наткнулся на эту идею в своей записной книжке и решил, что не перестану думать об этом, пока полностью не поиграю в нее. В итоге я написал два совершенно новых алгоритма, первый из которых оказался на несколько байт короче, после того, как вы проиграли около 25 байт с кучей хитов.
Как это работает
Сначала мы устанавливаем переменные aи bв 0. aявляется 4-битным двоичным массивом пар скобок, в котором мы сейчас находимся, и b16-разрядным двоичным массивом пар скобок, которые связаны вместе.
Далее мы перебираем каждый символ cв xи каждый символ dв '0123'. Сначала мы определяем , какой тип кронштейна cнаходится с e=c.charCodeAt()/26-1|0. Десятичные коды символов каждого типа скобок:
() => 40,41
<> => 60,62
[] => 91,93
{} => 123,125
Делив на 26, вычитая 1 и настил, мы сопоставляем их с 0, 1, 2 и 3 соответственно.
Далее мы проверяем, равно ли это число текущему значению d. Если это так, мы либо входим, либо выходим из dтипа th скобки, поэтому мы переключаем dбит th aс помощью a^=1<<d. Если это не так, но мы находимся внутри dскобки, нам нужно перевернуть eбит в d4-битной секции b. Это делается так:
b^=(a>>d&1)<<d*4+e
(a>>d&1)Возвращает dбит в a. Если мы находимся внутри dскобочного типа, это возвращает 1; в противном случае возвращается 0. Затем мы сдвигаем это влево на d*4+eбиты, а XOR b- на результат. Если мы находимся внутри dскобочного типа, то это XOR d*4+eбит b; в противном случае это ничего не делает.
В конце всего цикла bбудет содержаться число 1-бит, равное удвоенному желаемому возвращаемому значению. Но нам все еще нужно выяснить, сколько это битов. Вот где fвходит подфункция:
f=y=>y&&y%2+f(y>>1)
Если y0, это просто возвращает 0. В противном случае, он принимает последний бит yс y%2, а затем добавляет результат yповторного выполнения всех функций, кроме последнего, через функцию. Например:
f(y) => y && y%2 + f(y>>1)
f(0b1001101) => 1 + f(0b100110) = 4
f(0b100110) => 0 + f(0b10011) = 3
f(0b10011) => 1 + f(0b1001) = 3
f(0b1001) => 1 + f(0b100) = 2
f(0b100) => 0 + f(0b10) = 1
f(0b10) => 0 + f(0b1) = 1
f(0b1) => 1 + f(0b0) = 1
f(0b0) => 0 = 0
Мы пробегаем bэту функцию и делим результат на 2, и наш ответ есть.