Статические локальные переменные в Perl
Я ищу советы по передовым методам Perl. Я написал script, который имел сложное регулярное выражение:
my $regex = qr/complicated/;
# ...
sub foo {
# ...
if (/$regex/)
# ...
}
где foo
- это функция, которая часто называется, а $regex
не используется вне этой функции. Каков наилучший способ справиться с такими ситуациями? Я только хочу, чтобы это интерпретировалось один раз, поскольку оно долгое и сложное. Но кажется немного сомнительным иметь его в глобальном масштабе, поскольку он используется только в этом разделе. Есть ли разумный способ объявить его статическим?
Аналогичная проблема возникает и с другим, возможно, необоснованным глобальным. Он считывает текущую дату и время и форматирует его соответствующим образом. Это также используется много раз, и снова только в одной функции. Но в этом случае еще более важно, чтобы он не был повторно инициализирован, так как я хочу, чтобы все экземпляры даты-времени были одинаковыми от заданного вызова script, даже если минуты перевернутся во время выполнения.
На данный момент у меня есть что-то вроде
my ($regex, $DT);
sub driver {
$regex = qr/complicated/;
$DT = dateTime();
# ...
}
# ...
driver();
который, по крайней мере, немного отделяет его. Но, возможно, есть лучшие способы.
Опять же: я ищу правильный способ сделать это с точки зрения передовой практики и идиом Perl. Производительность хорошая, но читаемость и другие потребности имеют приоритет, если я не могу все.
Ответы
Ответ 1
Если вы используете perl 5.10+, используйте переменную state
.
use feature 'state';
# use 5.010; also works
sub womble {
state $foo = something_expensive();
return $foo ** 2;
}
будет вызывать только something_expensive
один раз.
Если вам нужно работать со старыми perls, используйте лексическую переменную во внешней области с дополнительной парой фигурных скобок:
{
my $foo = something_expensive();
sub womble {
return $foo ** 2;
}
}
это предотвращает утечку $foo
для всех, кроме womble
.
Ответ 2
Есть ли какая-либо интерполяция в шаблоне? Если нет, шаблон будет компилироваться один раз независимо от того, сколько раз выполняется qr//.
$ perl -Mre=debug -e'qr/foo/ for 1..10' 2>&1 | grep Compiling | wc -l
1
$ perl -Mre=debug -e'qr/foo$_/ for 1..10' 2>&1 | grep Compiling | wc -l
10
Даже если есть интерполяция, шаблон будет компилироваться только при изменении интерполированных переменных.
$ perl -Mre=debug -e'$x=123; qr/foo$x/ for 1..10;' 2>&1 | grep Compiling | wc -l
1
$ perl -Mre=debug -e'qr/foo$_/ for 1..10' 2>&1 | grep Compiling | wc -l
10
В противном случае вы можете использовать
{
my $re = qr/.../;
sub foo {
...
/$re/
...
}
}
или
use feature qw( state );
sub foo {
state $re = qr/.../;
...
/$re/
...
}
Ответ 3
Regexes можно указать с помощью модификатора "o", который говорит "только шаблон компиляции" - в третьем. издание Верблюда, см. с. 147
Ответ 4
Там state ключевое слово, которое может быть хорошо подходит для этой ситуации:
sub foo {
state $regex = /.../;
...
}
Ответ 5
Я хотел бы завершить отличный ответ ikegami
. Еще несколько слов, которые я хотел бы потратить на определение локальных переменных в pre 5.10 perl.
Посмотрим на простой пример кода:
#!/bin/env perl
use strict;
use warnings;
{ # local
my $local = "After Crying";
sub show { print $local,"\n"; }
} # local
sub show2;
show;
show2;
exit;
{ # local
my $local = "Solaris";
sub show2 { print $local,"\n"; }
} # local
Пользователь будет ожидать, что оба sub
будут печатать локальную переменную, но это неверно!
Выход:
After Crying
Use of uninitialized value $local in print at ./x.pl line 20.
Причина в том, что show2
анализируется, но инициализация локальной переменной не выполняется! (Конечно, если exit
удаляется и в конце добавляется show2
, Solaris
будет напечатано в третьей строке)
Это можно легко устранить:
{ # local
my $local;
BEGIN { $local = "Solaris"; }
sub show2 { print $local,"\n"; }
} # local
И теперь результат, который ожидался:
After Crying
Solaris
Но state
в 5.10+ - лучший выбор...
Надеюсь, это поможет!