Подтвердить что ты не робот

Статические локальные переменные в 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. Производительность хорошая, но читаемость и другие потребности имеют приоритет, если я не могу все.

4b9b3361

Ответ 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 .

Посмотрим на простой пример кода:

#!/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+ - лучший выбор...

Надеюсь, это поможет!