Вариант № 1: Не захватывать субтитры, содержащие обратную ссылку
$0
- это обратная ссылка 1 .
Если вы опускаете подкадр вокруг выражения, содержащего $0
, то код работает:
$_="bbaaaaawer"; / (.) $0**2..* / && print $/; # aaaaa
Тогда вы также можете опустить {}
.(Я вернусь к тому, почему вам иногда нужно вставить {}
позже в этом ответе.)
Но, возможно, вы написали вспомогательный захват вокруг выражения, содержащего обратную ссылку, потому что вы думали, что выдля последующей обработки потребовался дополнительный захват.
Часто есть другие способы сделать что-то.В вашем примере, возможно, вы хотели получить способ подсчитать количество повторений.Если это так, вы можете вместо этого написать:
$_="bbaaaaawer";
/ (.) $0**2..* /;
print $/.chars div $0.chars; # 5
Работа выполнена, без осложнений из следующих разделов.
Вариант № 2.Вспомогательный захват без изменения текущего объекта сопоставления во время сопоставления с шаблоном, содержащим обратную ссылку
Возможно, вам действительно нужно повторно захватить совпадение выражения, содержащего обратную ссылку.
Это все еще можно сделать без необходимости окружать $0
дополнительным захватом.Это избавит от проблем, обсуждаемых в третьем разделе ниже.
Вы можете использовать эту технику, если вам не нужны под-захваты выражения и , выражение не являетсяслишком сложный:
$_="bbaaaaawer";
/ (.) $<capture-when-done>=$0**2..* /;
print $<capture-when-done>.join; # aaaa
Этот подхват фиксирует результат соответствия выражения в именованном захвате, но избегает вставки дополнительного контекста подхвата вокруг выражения(именно это и вызывает сложности, обсуждаемые в следующем разделе).
К сожалению, хотя этот метод будет работать для выражения в вашем вопросе ($0**2..*
), он не будет работать, если выражение достаточно сложно, чтобы нуждаться вгруппировка.Это потому, что синтаксис $<foo>=[...]
не работает.Возможно, это можно исправить без ущерба для производительности или других проблем. 2
Опция № 3.Используйте сохраненную обратную ссылку внутри вспомогательного захвата
Наконец, мы приходим к технике, которую вы использовали в своем вопросе.
Доступен автоматическиобратные ссылки на вспомогательные снимки (например, $0
) не могут ссылаться на вспомогательные снимки, которые произошли за пределами вспомогательного захвата, в котором они записаны.
Итакесли по какой-либо причине вам необходимо создать вспомогательный захват (используя (...)
или <...>
), вы должны вручную сохранить обратную ссылку в переменной и использовать ее вместо этого.
Прежде чем мы получимВ заключительном разделе, подробно объясняющем, почему вы должны использовать переменную, давайте сначала завершим первоначальный ответ на ваш вопрос, покрыв окончательную морщинку.
{}
заставляет «публиковать» результаты матчей на данный момент
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * {}
, чтобы заставить :my $c=$0;
обновляться каждый раз, когда это достигается с использованием текущего механизма регулярных выражений / грамматики.Если вы его не напишите, то движку регулярных выражений не удастся обновить $c
до захвата 'a'
, и вместо этого он застрянет при захвате 'b'
.
Пожалуйста, прочитайте «Публикация» переменных соответствия по Rakudo .
Почему не может вспомогательный захват включает обратную ссылку на захваты, которые произошли снаружи этот вспомогательный захват?
Во-первых, вы должны принять во внимание, что сопоставление в P6 оптимизировано для случая вложенного сопоставления синтаксически, семантически и с точки зрения реализации.
В частности, есликогда вы пишете регулярное выражение или грамматику, вы пишете пронумерованный захват (с (...)
) или именованное правило / захват (с <foo>
), затем вы вставляете новый уровень в дерево подшаблонов, которыединамически сопоставляется / захватывается во время выполнения.
См. ответ jnthn , почему и Brad's , для некоторого обсуждения деталей.
ЧтоЯ добавлю к этим ответам (грубую!) Аналогию и еще одно обсуждениеПриходится использовать переменную и {}
.
Аналогия начинается с дерева подкаталогов в файловой системе:
/
a
b
c
d
Аналогия такова:
Приведенная выше структура каталогов соответствует результату завершенной операции сопоставления.
После завершения общего сопоставления или синтаксического анализа объект сопоставления $/
ссылается (аналогично) на корневой каталог. 3
Подкаталоги соответствуют подкадрам матча.
Нумерованные суб-совпадения / суб-захваты $0
и $1
на верхнем уровне операции сопоставления, показанные под этими пунктами, соответствуют подкаталогам a
и b
. Пронумерованные субкадры верхнего уровня $1
sub-match / sub-capture соответствуют подкаталогам c
и d
.
Во время сопоставления $/
относится к «текущему объекту сопоставления», который соответствует «текущему рабочему каталогу» .
Легко сослаться на подхват (подкаталог) текущего соответствия (текущий рабочий каталог).
Это невозможно ссылаться на подхват (подкаталог) вне текущего соответствия (текущий рабочий каталог), если вы не сохранили ссылку на него вне каталога (захвата) или его родителя. То есть P6 не включает аналог ..
или /
!
Если навигация по файловой системе не поддерживает эти обратные ссылки на корень, то единственное, что нужно сделать, - это создать переменную среды, в которой будет храниться определенный путь. Это примерно то, что делает сохранение захвата в переменной в регулярном выражении P6.
Центральная проблема заключается в том, что большинство машин, связанных с регулярными выражениями, относительно "текущего совпадения" . И это включает $/
, который относится к текущему совпадению и обратным ссылкам, таким как $0
, которые относятся к текущему совпадению.
Таким образом, в следующем примере, который запускается через tio.run здесь , легко отобразить 'bc'
или 'c'
с кодовым блоком, вставленным в третью пару символов ...
$_="abcd";
m/ ( ( . ) ( . ( . ) { say $/ } ( . ) ) ) /; # 「bc」 0 => 「c」
say $/; # 「abcd」 etc.
... но невозможно ссылаться на захваченный 「a」
в этой третьей паре паренов без сохранения захвата 「a」
в обычной переменной.
Вот один из способов посмотреть на вышеприведенный матч:
↓ Start TOP level $/
m/ ( ( . ) ( . ( . ) { say $/ } ( . ) ) ) /; # captures 「abcd」
↓ Start first sub-capture; TOP's $/[0]
( ) # captures 「abcd」
↓ Start first sub-sub-capture; TOP's $/[0][0]
( . ) # captures 「a」
↓ Start *second* sub-sub-capture; TOP's $/[0][1]
( ) # captures 「bcd」
↓ Start sub-sub-sub-capture; TOP's $/[0][1][0]
( . ) # captures 「c」
{ say $/ } # 「bc」 0 => 「c」
( . ) # captures 'd'
Если мы сосредоточимся на том, что $/
относится к вне регулярного выражения (а также непосредственно внутри регулярного выражения /.../
, но не внутри подпрограммы -captures ), затем , что $/
относится к общему Match
объекту, который в итоге захватывает 「abcd」
. (В аналогии с файловой системой этот конкретный $/
является корневым каталогом.)
$/
внутри кодового блока внутри второго подподдержки относится к объекту сопоставления более низкого уровня, а именно к тому, который в момент выполнения say $/
уже имел соответствует 「bc」
и будет захватывать 「bcd」
к концу общего матча.
Но не имеет встроенного способа для обозначения дополнительного захвата 'a'
или общего захвата (который в этот момент будет 'abc'
) изнутри окружения дополнительного захвата. кодовый блок.
Следовательно, вы должны сделать что-то вроде того, что вы сделали.
Возможное улучшение?
Что если в регулярных выражениях P6 был указан прямой аналог для определения корня?
Вот начальное сокращение, которое может иметь смысл. Давайте определим грамматику:
my $*TOP;
grammar g {
token TOP { { $*TOP := $/ } (.) {} <foo> }
token foo { <{$*TOP[0]}> }
}
say g.parse: 'aa' # 「aa」 0 => 「a」 foo => 「a」
Таким образом, возможно, может быть введена новая переменная, предназначенная только для чтения для пользовательского кода, которая привязана к общему объекту сопоставления во время операции сопоставления.
Но тогда это не только довольно уродливо (неспособно использовать удобную краткую обратную ссылку, такую как $0
), но переориентирует внимание на необходимость также вставлять {}
. И учитывая, что, вероятно, было бы абсурдно дорого переиздавать все дерево объектов совпадений после каждого атома, каждый возвращается на полный круг к текущему статус-кво. Если не считать исправлений, упомянутых в этом ответе, я думаю, что то, что реализовано в настоящее время, настолько хорошо, насколько вероятно.
Сноска
1 В текущем документе P6 не используется обычный термин регулярного выражения "обратная ссылка", но $0
, $1
и т. Д. Являются номерами обратных ссылок P6. Самое простое объяснение нумерованных обратных ссылок, которое я видел, это это ТАК о них с использованием другого диалекта регулярных выражений . В P6 они начинаются с $
вместо \
и нумеруются, начиная с 0
, а не 1
. Эквивалент \0
в других диалектах регулярных выражений равен $/
в P6. Кроме того, $0
является псевдонимом для $/[0]
, $1
для $/[1]
и т. Д.
2 Кто-то может подумать, что это сработает, но это не так:
$_="bbaaaaawer";
/ (.) $<doesn't-work>=[$0**2..*] /;
print $<doesn't-work>.join; # Use of Nil in string context
Кажется, что [...]
не означает "группа", но не вставляйте новый уровень захвата, как (...)
и <...>
, делайте "вместо этого" группируйте и не захватывайте ". Это делает $<doesn't-work>
в $<doesn't-work>=[$0**2..*]
бессмысленным. Возможно, это можно исправить, и, возможно, это следует исправить.
3 Текущий "переменная соответствия" doc говорит:
$/
- переменная соответствия. Он хранит результат последнего Regex
совпадения и поэтому обычно содержит объекты типа Match
.
(Fwiw $/
содержит List
из Match
объектов, если используется наречие типа :global
или :exhaustive
.)
Приведенное выше описание игнорирует очень важный вариант использования для $/
, который является его использованием во время сопоставления , и в этом случае он содержит результаты пока тока регулярное выражение.
По аналогии с нашей файловой системой, $/
подобен текущему рабочему каталогу - назовем его «текущим рабочим объектом сопоставления» или CWMO. За пределами операцией сопоставления CWMO ($/
) обычно является завершенный результат последнего последнего совпадения регулярного выражения или синтаксического анализа. (Я говорю «обычно», потому что он доступен для записи, поэтому код может изменить его всего за $/ = 42
.) Во время операций сопоставления (или действий ) CWMO доступен только для чтения для пользовательского пространства код и привязан к объекту Match
, созданному механизмом регулярных выражений / грамматики для текущего соответствия или правила / метода действия.