TL; DR Отложите оценку $<nr>
до оценки регулярного выражения. @ JoKing ++ предлагает в одну сторону . Другой способ - просто заключить замену в фигурные скобки ({$<nr>}
).
Что происходит, когда ваш исходный код вызывает subst
Перед тем, как Раку пытается вызвать подпрограмму subst
, он помещает вместе список аргументов для передачи на него.
Есть два значения. Первый - это регулярное выражение. Он не работает . Второе значение $<nr>
. Он оценивается как Nil
, потому что в начале программы переменная объекта текущего соответствия связана с чем-то, что утверждает, что ее значение равно Nil
, и любая попытка получить доступ к значению ключа внутри нее - $<nr>
- - также возвращает Nil
. Так что на этом этапе все пошло не так, прежде чем subst
запустится.
Как только Raku соберет этот список аргументов, он попытается вызвать subst
. Это успешно, и subst
запускается.
Чтобы получить следующий матч, subst
запускает регулярное выражение. Это обновляет текущую переменную объекта сопоставления $/
. Но уже слишком поздно что-либо менять в значении подстановки, которое уже было передано subst
.
С учетом совпадения, subst
затем просматривает аргумент подстановки. Он находит Nil
и действует соответственно.
Для секундного вызова subst
, $<nr>
принял значение из первого вызова subst
. И т. Д.
Два способа отложить оценку $<nr>
@ JoKing предлагает рассмотреть вопрос об использовании S///
. Эта конструкция сначала оценивает регулярное выражение (между первой парой /
с), а затем замену (между последней парой /
с). (Тот же принцип применяется, если вы используете другие допустимые синтаксисы S
, такие как S[...] = ...
.)
Если вы используете subst
, то, как объяснялось в предыдущем разделе, Raku собирает список аргументов для него прежде чем позвонить. Он находит регулярное выражение (которое он не запускает) и замыкание (которое он также не запускает). Затем он пытается вызвать subst
с этими аргументами и преуспевает в этом.
Далее, subst
начинает работать. Он получил код как для match (регулярное выражение), так и для замены (замыкание).
Он выполняет регулярное выражение как операцию сопоставления. Если регулярное выражение возвращает совпадение, то subst
запускает замыкание и использует значение, которое оно возвращает в качестве подстановки.
Таким образом, потому что мы перешли от передачи $<nr>
как голого значения, что означало, что оно было заморожено в Nil
, чтобы передать его, завернутый в замыкание, которое откладывало его оценку до тех пор, пока $/
не было установлено на совпадение с заполненной записью <nr>
, мы решили проблему.
Обратите внимание, что это работает только потому что тот, кто спроектировал / реализовал subst
, был достаточно умен и хорош, чтобы оба аргумента подстановки и могли быть формами Code
(регулярное выражение для соответствия и обычное закрытие для подстановки), если пользователь хочет этого. Затем он сначала запускает match и только , затем запускает закрытие замещения, если оно было передано, с использованием result этого последнего вызова в качестве окончательного замещения. Точно так же S///
работает, потому что , что был разработан, чтобы оценивать замену только после первой оценки замещения.