Встроенный код и директивы my и local
Во всех рассмотренных примерах результаты оказывались правильными только благодаря счастливой случайности: в наших регулярных выражениях либо не было возврата, либо возвраты за исполняемый код не приводили к сбоям в работе переменных my.
Выполненный код в результате возврата не отменишь, но нам и не надо было его отменять, как и результатов присвоенных в нем значений переменным.
Для корректной работы переменных внутри встроенного кода их надо локализовать внутри регулярного выражения. Это делается директивой local. Значения таких переменных при возврате за встроенный код, в котором им присваивались значения, восстанавливаются такими, какими были до выполнения этого кода. Такие переменные должны быть глобальными, т.е. созданными не директивой my. Если в программе используется директива use strict, то глобальную переменную можно создать с помощью ключевого слова our. Тогда директива local внутри регулярного выражения делает из этой переменной как бы стек ее значений: при присваивании ей значения во встроенном коде оно наслаивается поверх предыдущего значения этой переменной, а при возврате назад восстанавливается предыдущее значение. После конца работы регулярного выражения эта переменная восстанавливает свое значение, которое имела перед входом в регулярное выражение.
Рассмотрим такой пример:
#!/usr/bin/perl -w use strict;
$_='ab'; our $o=1; my $m=1; / (?: a (?{ $o++; $m++ }) | ab (?{ print "\$o=$o, \$m=$m" }) ) $ /x;
Регулярное выражение содержит конструкцию выбора:
/(a|ab)$/
Вначале будет найден символ a и выберется первая ветка условного шаблона, при этом переменные $o и $m увеличатся на единицу. Но затем этот выбор будет отменен, поскольку за символом a должен идти символ b, и управление получит вторая альтернатива конструкции выбора, в которой будут распечатаны значения переменных $o и $m. На печать выйдет:
$o=2, $m=2
В этом примере различия в работе этих переменных нет. Теперь локализуем глобальную переменную $o в регулярном выражении:
#!/usr/bin/perl -w use strict;
$_='ab'; our $o=1; my $m=1; / (?{ local $o }) (?: a (?{ $o++; $m++ })| ab (?{ print "\$o=$o, \$m=$m" }) ) $ /x;
На сей раз напечатается это:
$o=1, $m=2
Этот пример показывает работу директивы local во встроенном коде.
Не забывайте в конце регулярного выражения запоминать значения локализованных переменных, т.к. при выходе за регулярное выражение они утрачиваются.
Если объявить переменную my внутри регулярного выражения, то в других блоках встроенного кода переменная с этим именем не будет соответствовать той переменной, что была объявлена. Она либо будет создана заново как глобальная, либо, если переменная с этим именем уже существует до регулярного выражения, она будет отождествлена с ней. При возврате такая переменная не будет восстанавливать свои старые значения. Вот два примера:
$_='ab'; our $o=1; / (?{ my $m=10; local $o }) (?: a (?{ $o++; $m++ })| ab (?{ print "\$o=$o, \$m=$m" }) ) $ /x;
Напечатается
$o=1, $m=1
Мы видим, что во втором встроенном коде переменная $m создалась заново с неопределенным значением, затем к нему применили ++ и получили 1. И это значение потом использовалось при печати.
$_='ab'; our $o=1; my $m=10; / (?{ my $m=5; local $o }) (?: a (?{ $o++; $m++ })| ab (?{ print "\$o=$o, \$m=$m" }) ) $ /x;
Здесь напечатается
$o=1, $m=11
Во втором и третьем встроенном коде использовалась переменная $m, которая была создана до регулярного выражения.
Директиву local можно комбинировать с присваиванием значения этой переменной.
Например:
local ($ctop) = $ctop+1;