Веселый вопрос! Принятый ответ хорошо, но вот другой способ сделать это, потому что всегда полезно увидеть другое решение.
#!/usr/bin/perl
use strict;
use warnings;
my $string = '((((((...)))(((...))))))';
my (@out, @match);
while ($string =~ m/([()])/g) {
my $p = pos($string) - 1;
if ($1 eq '(') {
push @out, [$p];
push @match, $#out;
}
else {
die "mismatched paren at char $p\n"
unless @match;
$out[pop @match][1] = $p;
}
}
for (@out) { print "@$_\n" }
exit(0);
Выход в точности соответствует желаемому результату. Умирает от несоответствующих скобок (что может сделать код Чоробы с соответствующим тестом в блоке elsif). Несоответствующие круглые скобки приводят к строкам без второго числа, и они также будут остаточными в @match
после цикла while.
Я решил немного использовать сопоставление с шаблоном Perl вместо того, чтобы разбивать строку на отдельные символы и перебирать их все. Вместо этого я сопоставляю каждую открывающую или закрывающую скобку по очереди, используя модификатор «g». Таким образом, цикл повторяется только по интересующим символам. Функция pos()
в $string
возвращает точку после последнего совпадения, поэтому мне нужно вычесть единицу, чтобы получить вывод на основе нуля.
Другое ключевое отличие состоит в том, что я накапливаюсь в @out
и отслеживаю соответствующее закрытие, отмечая последний индекс @out
, нажимая на @match
. Затем я выскакиваю @match
, когда нахожу закрывающие скобки, и добавляю второй элемент в подмассив в @out
в этой позиции. Это устраняет необходимость сортировки окончательного результата, поскольку @out
уже находится в порядке открывающих скобок.