Замена строки на .subst в цикле for - PullRequest
8 голосов
/ 22 января 2020

Я хотел бы сделать подстановку строк в блоке for, используя именованный захват. Я ожидал получить цифры 1,2,3 в качестве вывода. Но это Nil для первого запуска, а затем 1 и 2 для 2-го и 3-го запуска. Как правильно использовать .subst в конструкции l oop? Я вижу такое же поведение при использовании конструкции map вместо for l oop. Это работает, как и ожидалось, если я заменю фиксированное строковое значение.

for <a1 b2 c3> -> $var {
    say $var;
    say $var.subst(/.$<nr>=(\d)/, $<nr>); #.subst(/.$<nr>=(\d)/, 'X'); #OK      
}

#`[
This is Rakudo version 2019.11 built on MoarVM version 2019.11   
Output:

a1
Use of Nil in string context
  in block  at test3.pl6 line 3

b2
1
c3
2
]

1 Ответ

9 голосов
/ 22 января 2020

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/// работает, потому что , что был разработан, чтобы оценивать замену только после первой оценки замещения.

...