perlsub
СОДЕРЖАНИЕ
- ИМЯ
- СИНТЕКС
- ОПИСАНИЕ
- Подписи
- Переменные-только-для-подпрограммы с помощью my()
- Постоянные переменные-только-для-подпрограммы
- Временные значения с помощью local()
- Подпрограммы-lvalues
- Лексические подпрограммы
- Передача элементов таблицы символов (typeglobs)
- Когда всё-таки использовать local()
- Передача по ссылке
- Прототипы
- Постоянные функции
- Переопределение встроенных функций
- Автозагрузка
- Атрибуты подпрограмм
- СМОТРИТЕ ТАКЖЕ
ИМЯ
perlsub - Подпрограммы Perl
СИНТЕКС
Для объявления подпрограмм:
sub NAME; # A "forward" declaration.
sub NAME(PROTO); # ditto, but with prototypes
sub NAME : ATTRS; # with attributes
sub NAME(PROTO) : ATTRS; # with attributes and prototypes
sub NAME BLOCK # A declaration and a definition.
sub NAME(PROTO) BLOCK # ditto, but with prototypes
sub NAME : ATTRS BLOCK # with attributes
sub NAME(PROTO) : ATTRS BLOCK # with prototypes and attributes
use feature 'signatures';
sub NAME(SIG) BLOCK # with signature
sub NAME :ATTRS (SIG) BLOCK # with signature, attributes
sub NAME :prototype(PROTO) (SIG) BLOCK # with signature, prototype Для определения анонимной подпрограммы во время выполнения:
$subref = sub BLOCK; # no proto
$subref = sub (PROTO) BLOCK; # with proto
$subref = sub : ATTRS BLOCK; # with attributes
$subref = sub (PROTO) : ATTRS BLOCK; # with proto and attributes
use feature 'signatures';
$subref = sub (SIG) BLOCK; # with signature
$subref = sub : ATTRS(SIG) BLOCK; # with signature, attributes Для импорта подпрограмм:
use MODULE qw(NAME1 NAME2 NAME3); Для вызова подпрограмм:
NAME(LIST); # & is optional with parentheses.
NAME LIST; # Parentheses optional if predeclared/imported.
&NAME(LIST); # Circumvent prototypes.
&NAME; # Makes current @_ visible to called subroutine. ОПИСАНИЕ
Как и во многих языках, Perl предоставляет возможность определять пользовательские подпрограммы. Они могут быть размещены где угодно в основной программе, загружены из других файлов с помощью ключевых слов do, require, или use, или сгенерированы на лету с помощью eval или анонимных подпрограмм. Вы даже можете вызвать функцию косвенно, используя переменную, содержащую её имя или ссылку на код.
Модель Perl для вызова функции и возвращаемых значений проста: все функции передаются в виде параметра одного единственного плоского списка скаляров, и все функции аналогично возвращают вызывающей стороне один единственный плоский список скаляров. Любые массивы или хэши в этих списках вызова и возврата будут сжаты, потеряв свою идентичность — но вы всегда можете использовать передачу по ссылке, чтобы избежать этого. В списках вызова и возврата может быть любое количество скалярных элементов. (Функция без явного оператора возврата часто называется подпрограммой, но с точки зрения Perl нет никакой разницы.)
Все переданные аргументы отображаются в массиве @_. (Они также могут отображаться в лексических переменных, введенных подписью; см. "Подписи" ниже.) Следовательно, если вы вызвали функцию с двумя аргументами, они будут храниться в $_[0] и $_[1]. Массив @_ — это локальный массив, но его элементы являются псевдонимами для фактических скалярных параметров. В частности, если элемент $_[0] обновляется, соответствующий аргумент обновляется (или возникает ошибка, если он не может быть обновлен). Если аргумент является элементом массива или хэша, который не существовал при вызове функции, этот элемент создается только тогда (и только если) он модифицируется или к нему берётся ссылка. (В некоторых более ранних версиях Perl элемент создавался независимо от того, присваивался ли он.) Присвоение всему массиву @_ удаляет это псевдонимирование и не обновляет аргументы.
Оператор return может использоваться для выхода из подпрограммы, при необходимости указывая возвращаемое значение, которое будет вычислено в соответствующем контексте (список, скаляр или пустота) в зависимости от контекста вызова подпрограммы. Если вы не указываете возвращаемого значения, подпрограмма возвращает пустой список в контексте списка, неопределенное значение в скалярном контексте или ничего в пустом контексте. Если вы возвращаете один или несколько агрегатов (массивы и хэши), они будут сжаты вместе в один большой неразличимый список.
Если return не найден и если последним оператором является выражение, его значение возвращается. Если последним оператором является структура управления циклом, например foreach или while, возвращаемое значение не определено. Пустая подпрограмма возвращает пустой список.
Помимо экспериментальной возможности (см. "Подписи" ниже), Perl не имеет именованных формальных параметров. На практике всё, что вам нужно сделать, это назначить список my() этих параметров. Переменные, которые не объявлены как частные, являются глобальными переменными. Для подробного изучения создания частных переменных см. "Переменные-только-для-подпрограммы с помощью my()" и "Временные значения с помощью local()". Для создания защищенных сред для набора функций в отдельном пакете (и, вероятно, отдельном файле) см. "Пакеты" в perlmod.
Пример:
sub max {
my $max = shift(@_);
foreach $foo (@_) {
$max = $foo if $max < $foo;
}
return $max;
}
$bestday = max($mon,$tue,$wed,$thu,$fri); Пример:
# get a line, combining continuation lines
# that start with whitespace
sub get_line {
$thisline = $lookahead; # global variables!
LINE: while (defined($lookahead = <STDIN>)) {
if ($lookahead =~ /^[ \t]/) {
$thisline .= $lookahead;
}
else {
last LINE;
}
}
return $thisline;
}
$lookahead = <STDIN>; # get first line
while (defined($line = get_line())) {
...
} Присваивание списку частных переменных для именования ваших аргументов:
sub maybeset {
my($key, $value) = @_;
$Foo{$key} = $value unless $Foo{$key};
} Поскольку присвоение копирует значения, это также приводит к преобразованию передачи по ссылке в передачу по значению. В противном случае функция может свободно выполнять изменения на месте в @_ и изменять значения вызывающей стороны.
upcase_in($v1, $v2); # this changes $v1 and $v2
sub upcase_in {
for (@_) { tr/a-z/A-Z/ }
} Конечно, вам запрещено изменять константы таким образом. Если аргумент был фактически литералом, и вы попытались его изменить, вы получите (предположительно, фатальную) ошибку. Например, это не сработает:
upcase_in("frederick"); Гораздо безопаснее, если функция upcase_in() была бы написана так, чтобы возвращать копию своих параметров, а не изменять их на месте:
($v3, $v4) = upcase($v1, $v2); # this doesn't change $v1 and $v2
sub upcase {
return unless defined wantarray; # void context, do nothing
my @parms = @_;
for (@parms) { tr/a-z/A-Z/ }
return wantarray ? @parms : $parms[0];
} Обратите внимание, как эта (непрототипированная) функция не заботится о том, были ли ей переданы реальные скаляры или массивы. Perl рассматривает все аргументы как один большой, длинный, плоский список параметров в @_. В этом случае преимущества простого стиля передачи аргументов Perl очевидны. Функция upcase() будет работать без изменений в определении upcase() даже если мы передадим ей такие вещи:
@newlist = upcase(@list1, @list2);
@newlist = upcase( split /:/, $var ); Однако не стоит пытаться сделать это:
(@a, @b) = upcase(@list1, @list2); Как и плоский входящий список параметров, возвращаемый список также сжимается при возвращении. Таким образом, всё, что вы сделали, — это сохранили всё в @a и сделали @b пустым. См. "Передача по ссылке" для альтернатив.
Подпрограмму можно вызвать с явным префиксом &. Префикс & является необязательным в современном Perl, как и скобки, если подпрограмма была предложена. Префикс & не является необязательным при простом именовании подпрограммы, например, при использовании ее как аргумента для defined() или undef(). Он также не является необязательным, когда вы хотите выполнить косвенный вызов подпрограммы с именем или ссылкой на подпрограмму, используя конструкции &$subref() или &{$subref}(), хотя обозначение $subref->() решает эту проблему. См. perlref для получения дополнительной информации об этом.
Подпрограммы могут вызываться рекурсивно. Если подпрограмма вызывается в форме &, список аргументов необязателен, и если он опущен, массив @_ для подпрограммы не создается: массив @_ на момент вызова виден подпрограмме вместо него. Это механизм повышения эффективности, который новым пользователям следует избегать.
&foo(1,2,3); # pass three arguments
foo(1,2,3); # the same
foo(); # pass a null list
&foo(); # the same
&foo; # foo() get current args, like foo(@_) !!
foo; # like foo() IFF sub foo predeclared, else "foo" Форма & не только делает список аргументов необязательным, но также отключает проверку прототипов аргументов, которые вы предоставляете. Это отчасти по историческим причинам, отчасти для удобного обхода, если вы знаете, что делаете. См. "Прототипы" ниже.
Начиная с Perl 5.16.0, токен __SUB__ доступен в use feature 'current_sub' и use 5.16.0. Он будет вычисляться как ссылка на текущую подпрограмму, что позволяет выполнять рекурсивные вызовы, не зная имени вашей подпрограммы.
use 5.16.0;
my $factorial = sub {
my ($x) = @_;
return 1 if $x == 1;
return($x * __SUB__->( $x - 1 ) );
}; Поведение __SUB__ внутри блока кода регулярного выражения (например, /(?{...})/) может меняться.
Подпрограммы, имена которых состоят из заглавных букв, зарезервированы для ядра Perl, как и модули, имена которых состоят из строчных букв. Подпрограмма с заглавными буквами — это нестрогое соглашение о том, что она будет вызываться косвенно самой системой выполнения, обычно из-за произошедшего события. Подпрограммы, имена которых начинаются с открывающей скобки, также зарезервированы аналогичным образом. Ниже приведен список некоторых подпрограмм, которые в настоящее время выполняют специальные заранее определённые действия.
- документировано позднее в этом документе
-
AUTOLOAD - документировано в perlmod
-
CLONE,CLONE_SKIP - документировано в perlobj
-
DESTROY,DOES - документировано в perltie
-
BINMODE,CLEAR,CLOSE,DELETE,DESTROY,EOF,EXISTS,EXTEND,FETCH,FETCHSIZE,FILENO,FIRSTKEY,GETC,NEXTKEY,OPEN,POP,PRINT,PRINTF,PUSH,READ,READLINE,SCALAR,SEEK,SHIFT,SPLICE,STORE,STORESIZE,TELL,TIEARRAY,TIEHANDLE,TIEHASH,TIESCALAR,UNSHIFT,UNTIE,WRITE - документировано в PerlIO::via
-
BINMODE,CLEARERR,CLOSE,EOF,ERROR,FDOPEN,FILENO,FILL,FLUSH,OPEN,POPPED,PUSHED,READ,SEEK,SETLINEBUF,SYSOPEN,TELL,UNREAD,UTF8,WRITE - документировано в perlfunc
- документировано в UNIVERSAL
-
VERSION - документировано в perldebguts
-
DB::DB,DB::sub,DB::lsub,DB::goto,DB::postponed - недокументировано, используется внутри функции overload
-
любые, начинающиеся с
(
Подпрограммы BEGIN, UNITCHECK, CHECK, INIT и END — это не столько подпрограммы, сколько именованные специальные блоки кода, из которых в пакете может быть более одного, и которые вы не можете вызывать явно. См. "BEGIN, UNITCHECK, CHECK, INIT и END" в perlmod
Подписи
ПРЕДУПРЕЖДЕНИЕ: Подписи подпрограмм находятся в стадии эксперимента. Эта функция может быть изменена или удалена в будущих версиях Perl.
Perl имеет экспериментальную возможность, которая позволяет формальным параметрам подпрограммы быть представленными специальным синтаксисом, отделенным от процедурного кода тела подпрограммы. Список формальных параметров известен как подпись. Сначала необходимо включить эту возможность с помощью прагматического объявления use feature 'signatures', и это приведет к предупреждению, если категория предупреждений «experimental::signatures» не отключена.
Подпись является частью тела подпрограммы. Обычно тело подпрограммы просто представляет собой блочный код в фигурных скобках, но при использовании подписи подпись — это список в скобках, который ставится непосредственно перед блоком, после любого имени или атрибутов.
Например,
sub foo :lvalue ($a, $b = 1, @c) { .... } Подпись объявляет лексические переменные, которые находятся в области действия блока. Когда подпрограмма вызывается, подпись сначала берет под контроль. Она заполняет переменные подписи из списка аргументов, которые были переданы. Если список аргументов не соответствует требованиям подписи, то будет выброшено исключение. Когда обработка подписи завершена, управление переходит к блоку.
Позиционные параметры обрабатываются путем простого именования скалярных переменных в подписи. Например,
sub foo ($left, $right) {
return $left + $right;
} принимает два позиционных параметра, которые должны быть заполнены во время выполнения двумя аргументами. По умолчанию параметры являются обязательными, и не разрешается передавать больше аргументов, чем ожидается. Таким образом, вышеприведенное эквивалентно
sub foo {
die "Too many arguments for subroutine" unless @_ <= 2;
die "Too few arguments for subroutine" unless @_ >= 2;
my $left = $_[0];
my $right = $_[1];
return $left + $right;
} Аргумент может быть пропущен, если опустить основную часть имени из объявления параметра, оставив только символ $. Например,
sub foo ($first, $, $third) {
return "first=$first, third=$third";
} Хотя игнорируемый аргумент не попадает в переменную, для вызывающей стороны все равно обязательно передать его.
Позиционный параметр делается необязательным, если ему присваивается значение по умолчанию, отделенное от имени параметра через =:
sub foo ($left, $right = 0) {
return $left + $right;
} Вышеупомянутая подпрограмма может быть вызвана с одним или двумя аргументами. Выражение значения по умолчанию вычисляется при вызове подпрограммы, поэтому оно может предоставлять разные значения по умолчанию для разных вызовов. Оно вычисляется только в том случае, если аргумент был фактически опущен из вызова. Например,
my $auto_id = 0;
sub foo ($thing, $id = $auto_id++) {
print "$thing has ID $id";
} автоматически присваивает разные последовательные идентификаторы вещам, для которых вызывающая сторона не предоставила идентификатор. Выражение значения по умолчанию также может ссылаться на параметры, стоящие ранее в подписи, что делает значение по умолчанию для одного параметра зависящим от предыдущих параметров. Например,
sub foo ($first_name, $surname, $nickname = $first_name) {
print "$first_name $surname is known as \"$nickname\"";
} Необязательный параметр может быть безымянным так же, как и обязательный параметр. Например,
sub foo ($thing, $ = 1) {
print $thing;
} Значение параметра по умолчанию все равно будет вычислено, если соответствующий аргумент не указан, даже если значение нигде не будет сохранено. Это в случае, если его вычисление имеет важные побочные эффекты. Однако оно будет вычислено в контексте «void», поэтому, если оно не имеет побочных эффектов и не является тривиальным, оно сгенерирует предупреждение, если категория предупреждений «void» включена. Если значение по умолчанию необязательного безымянного параметра не важно, оно может быть опущено так же, как и имя параметра:
sub foo ($thing, $=) {
print $thing;
} Необязательные позиционные параметры должны следовать за всеми обязательными позиционными параметрами. (Если нет обязательных позиционных параметров, то необязательные позиционные параметры могут быть первым элементом в подписи.) Если есть несколько необязательных позиционных параметров и не достаточно аргументов для их заполнения, они будут заполнены слева направо.
После позиционных параметров дополнительные аргументы могут быть захвачены в параметре «slurpy». Самый простой вид этого — это просто массив переменных:
sub foo ($filter, @inputs) {
print $filter->($_) foreach @inputs;
} При наличии параметра «slurpy» в подписи нет верхнего предела на количество аргументов, которые могут быть переданы. Массив параметра «slurpy» может быть безымянным, как и позиционный параметр, в этом случае его единственным эффектом будет отключение ограничения аргументов, которое в противном случае применяется:
sub foo ($thing, @) {
print $thing;
} Параметр «slurpy» может вместо этого быть хешем, в этом случае аргументы, доступные для него, интерпретируются как чередующиеся ключи и значения. Должно быть столько же ключей, сколько значений: если есть нечетное количество аргументов, то будет выброшено исключение. Ключи будут приведены к строковому виду, и если есть дубликаты, то более поздний экземпляр имеет приоритет над более ранним, как и при стандартном построении хеша.
sub foo ($filter, %inputs) {
print $filter->($_, $inputs{$_}) foreach sort keys %inputs;
} Параметр «slurpy» типа хеш может быть безымянным, как и другие типы параметров. Он все равно требует, чтобы количество аргументов, доступных для него, было четным, даже если они не помещаются в переменную.
sub foo ($thing, %) {
print $thing;
} Параметр «slurpy», будь то массив или хеш, должен быть последним элементом в подписи. Он может следовать за обязательными и необязательными позиционными параметрами; он также может быть единственным элементом в подписи. Параметры «slurpy» не могут иметь значений по умолчанию: если для них не передано никаких аргументов, вы получаете пустой массив или пустой хеш.
Подпись может быть совершенно пустой, в этом случае все, что она делает, — это проверяет, что вызывающая сторона не передала никаких аргументов:
sub foo () {
return 123;
} При использовании подписи аргументы по-прежнему доступны в специальной переменной массива @_, в дополнение к лексическим переменным подписи. Существует разница между двумя способами доступа к аргументам: @_ сопоставляет аргументы, но переменные подписи получают копии аргументов. Таким образом, запись в переменную подписи изменяет только эту переменную и не влияет на переменные вызывающей стороны, но запись в элемент @_ изменяет то, что вызывающая сторона использовала для передачи этого аргумента.
Существует потенциальная синтаксическая неоднозначность между подписями и прототипами (см. "Прототипы"), так как обе начинаются с открывающей скобки и обе могут появляться в некоторых одинаковых местах, например, непосредственно после имени в объявлении подпрограммы. По историческим причинам, когда подписи не включены, любая открывающая скобка в таком контексте будет вызывать очень снисходительную обработку синтаксического анализа прототипов. Большинство подписей будут интерпретироваться как прототипы в этих обстоятельствах, но не будут допустимыми прототипами. (Допустимый прототип не может содержать ни одного буквенного символа.) Это приведет к несколько запутанным сообщениям об ошибках.
Чтобы избежать неоднозначности, когда подписи включены, специальный синтаксис для прототипов отключен. Нет попыток угадать, предназначен ли скобочный блок для прототипа или подписи. Чтобы присвоить подпрограмме прототип в этих условиях, используйте атрибут прототипа. Например,
sub foo :prototype($) { $_[0] } Полностью возможно, чтобы подпрограмма имела как прототип, так и подпись. Они выполняют разные функции: прототип влияет на компиляцию вызовов подпрограммы, а подпись помещает значения аргументов в лексические переменные во время выполнения. Поэтому вы можете написать
sub foo :prototype($$) ($left, $right) {
return $left + $right;
} Атрибут прототипа и любые другие атрибуты должны стоять перед подписью. Подпись всегда непосредственно предшествует блоку тела подпрограммы.
Приватные переменные через my()
Синтаксис:
my $foo; # declare $foo lexically local
my (@wid, %get); # declare list of variables local
my $foo = "flurp"; # declare $foo lexical, and init it
my @oof = @bar; # declare @oof lexical, and init it
my $x : Foo = $y; # similar, with an attribute applied ПРЕДУПРЕЖДЕНИЕ: Использование списков атрибутов в объявлениях my все еще находится в стадии развития. Текущие семантика и интерфейс могут быть изменены. См. attributes и Attribute::Handlers.
Оператор my объявляет перечисленные переменные как лексически ограниченные внутри окружающего блока, условного (if/unless/elsif/else), цикла (for/foreach/while/until/continue), подпрограммы, eval, или do/require/use файла. Если перечислено более одного значения, список должен быть заключён в скобки. Все перечисленные элементы должны быть допустимыми левыми значениями. Только буквенно-цифровые идентификаторы могут быть лексически ограничены — магические встроенные функции, такие как $/ в настоящее время должны быть localзованы с помощью local.
В отличие от динамических переменных, созданных оператором local, лексические переменные, объявленные с помощью my, полностью скрыты от внешнего мира, включая любые вызываемые подпрограммы. Это справедливо, если это та же подпрограмма, вызываемая из себя или извне — каждый вызов получает свою копию.
Это не означает, что переменная my , объявленная в статически окружающем лексическом пространстве, будет невидимой. Только динамические области видимости отрезаны. Например, функция bumpx() ниже имеет доступ к лексической переменной $x, потому что и my, и sub произошли в одной области видимости, предположительно области видимости файла.
my $x = 10;
sub bumpx { $x++ } Однако, eval() может видеть лексические переменные области видимости, в которой она оценивается, при условии, что имена не скрыты объявлениями внутри самого eval(). Смотрите perlref.
Список параметров my() можно присвоить, если это необходимо, что позволяет инициализировать переменные. (Если для определённой переменной не задана инициализация, она создаётся с неопределённым значением.) Обычно это используется для именования входных параметров подпрограммы. Примеры:
$arg = "fred"; # "global" variable
$n = cube_root(27);
print "$arg thinks the root is $n\n";
fred thinks the root is 3
sub cube_root {
my $arg = shift; # name doesn't matter
$arg **= 1/3;
return $arg;
} Оператор my является просто модификатором чего-то, к чему можно присвоить значение. Таким образом, при присваивании переменным в списке аргументов my не изменяет, рассматриваются ли эти переменные как скаляр или массив. Таким образом
my ($foo) = <STDIN>; # WRONG?
my @FOO = <STDIN>; оба обеспечивают контекст списка для правой части, в то время как
my $foo = <STDIN>; обеспечивает скалярный контекст. Но следующее объявляет только одну переменную:
my $foo, $bar = 1; # WRONG Это имеет тот же эффект, что и
my $foo;
$bar = 1; Объявленная переменная не вводится (не видна) до выполнения текущей инструкции. Таким образом,
my $x = $x; может быть использована для инициализации новой $x значением старой $x, а выражение
my $x = 123 and $x == 123 ложно, если старая $x не имела значение 123.
Лексические области видимости управляющих структур не ограничены точно фигурными скобками, ограничивающими контролируемые блоки; выражения управления также входят в эту область видимости. Таким образом, в цикле
while (my $line = <>) {
$line = lc $line;
} continue {
print $line;
} область видимости $line простирается от её объявления до остальной части конструкции цикла (включая continue предложение), но не выходит за её пределы. Аналогично, в условном операторе
if ((my $answer = <STDIN>) =~ /^yes$/i) {
user_agrees();
} elsif ($answer =~ /^no$/i) {
user_disagrees();
} else {
chomp $answer;
die "'$answer' is neither 'yes' nor 'no'";
} область видимости $answer простирается от её объявления до остальной части этого условного оператора, включая любые elsif и else предложения, но не выходит за её пределы. Смотрите "Простые операторы" в perlsyn для получения информации об области видимости переменных в операторах с модификаторами.
Цикл foreach по умолчанию определяет область видимости переменной индекса динамически, подобно local. Однако, если переменная индекса предваряется ключевым словом my, или если в области видимости уже существует лексическая переменная с таким именем, то вместо этого создаётся новая лексическая переменная. Таким образом, в цикле
for my $i (1, 2, 3) {
some_function();
} область видимости $i простирается до конца цикла, но не выходит за его пределы, что делает значение $i недоступным в some_function().
Некоторые пользователи могут пожелать поощрять использование лексически ограниченных переменных. В качестве помощи в обнаружении неявного использования упакованных переменных, которые всегда являются глобальными, если вы говорите
use strict 'vars'; тогда любая переменная, упомянутая оттуда до конца окружающего блока, должна либо ссылаться на лексическую переменную, быть предварительно объявлена через our или use vars, или же должна быть полностью квалифицирована именем пакета. В противном случае происходит ошибка компиляции. Внутренний блок может отменить это с помощью no strict 'vars'.
Оператор my имеет как эффект времени компиляции, так и времени выполнения. Во время компиляции компилятор обращает на него внимание. Основная полезность этого заключается в том, чтобы утихомирить use strict 'vars', но это также важно для генерации замыканий, как подробно описано в perlref. Фактическая инициализация откладывается до времени выполнения, поэтому она выполняется в соответствующее время, например, каждый раз при прохождении цикла.
Переменные, объявленные с my, не входят ни в один пакет и, следовательно, никогда не квалифицируются полностью именем пакета. В частности, вам не разрешается пытаться сделать переменную пакета (или другую глобальную) лексической:
my $pack::var; # ERROR! Illegal syntax Фактически, динамические переменные (также известные как переменные пакета или глобальные) по-прежнему доступны с помощью обозначения полной квалификации :: даже при том, что лексическая переменная с тем же именем также видна:
package main;
local $x = 10;
my $x = 20;
print "$x and $::x\n"; Это выведет 20 и 10.
Вы можете объявлять my переменные в самой внешней области видимости файла, чтобы скрыть такие идентификаторы от внешнего мира этого файла. Это аналогично статическим переменным C, используемым на уровне файла. Для подпрограммы это требует использования замыкания (анонимной функции, которая обращается к окружающим лексическим переменным). Если вы хотите создать закрытую подпрограмму, которую нельзя вызвать извне этого блока, она может объявить лексическую переменную, содержащую ссылку на анонимную подпрограмму:
my $secret_version = '1.001-beta';
my $secret_sub = sub { print $secret_version };
&$secret_sub(); Пока эта ссылка не возвращается какой-либо функцией внутри модуля, ни один внешний модуль не может видеть подпрограмму, потому что её имя не находится в таблице символов ни одного пакета. Помните, что её не на самом деле называют $some_pack::secret_version или что-либо подобное; это просто $secret_version, без квалификации и неквалифицируемая.
Однако это не работает с методами объектов; все методы объектов должны находиться в таблице символов какого-либо пакета, чтобы их можно было найти. Смотрите "Шаблоны функций" в perlref для обходного пути.
Постоянные Приватные Переменные
Существует два способа создания постоянных приватных переменных в Perl 5.10. Во-первых, вы можете просто использовать функцию state . Или же вы можете использовать замыкания, если хотите сохранить совместимость со старыми версиями, более ранними чем 5.10.
Постоянные переменные с помощью state()
Начиная с Perl 5.10.0, вы можете объявлять переменные с ключевым словом state вместо my. Однако для этого необходимо предварительно включить эту функцию, либо с помощью псевдонима feature, либо с помощью -E в однострочных выражениях (смотрите feature). Начиная с Perl 5.16, форма CORE::state не требует псевдонима feature.
Ключевое слово state создаёт лексическую переменную (следующую тем же правилам области видимости, что и my) , которая сохраняется от одного вызова подпрограммы к следующему. Если переменная state находится внутри анонимной подпрограммы, то каждая копия подпрограммы имеет свою собственную копию переменной state. Однако значение переменной state всё равно будет сохраняться между вызовами одной и той же копии анонимной подпрограммы. (Не забывайте, что sub { ... } создаёт новую подпрограмму каждый раз при выполнении).
Например, следующий код поддерживает приватный счётчик, увеличиваемый каждый раз при вызове функции gimme_another():
use feature 'state';
sub gimme_another { state $x; return ++$x } А этот пример использует анонимные подпрограммы для создания отдельных счётчиков:
use feature 'state';
sub create_counter {
return sub { state $x; return ++$x }
} Также, поскольку $x является лексической, её нельзя достичь или изменить с помощью любого кода Perl, находящегося вне неё.
При сочетании с объявлением переменных простое присваивание переменным state (как в state $x = 42) выполняется только в первый раз. При последующих оценках таких операторов присваивание игнорируется. Поведение присваивания к объявлениям state, где левая часть присваивания включает скобки, в настоящее время не определено.
Постоянные переменные с замыканиями
Просто потому, что лексическая переменная лексически (также называется статически) ограничена своим окружающим блоком, eval, или do ФАЙЛ, это не значит, что внутри функции она работает как статическая переменная в С. Обычно она работает больше как автоматическая переменная в С, но с неявным сборщиком мусора.
В отличие от локальных переменных в C или C++, лексические переменные Perl не обязательно перерабатываются только потому, что область видимости завершена. Если что-то более постоянное всё ещё осведомлено о лексической переменной, она останется. Пока что-то другое ссылается на лексическую переменную, она не будет освобождена — и это правильно. Вы бы не хотели, чтобы память освобождалась, пока вы её не закончите использовать, или оставалась после завершения.
Это означает, что вы можете передавать или сохранять ссылки на лексические переменные, в то время как возвращение указателя на автоматическую переменную C — серьёзная ошибка. Это также даёт нам способ моделирования статических переменных функций C. Вот механизм предоставления функции приватных переменных с лексической областью видимости и статическим сроком жизни. Если вы действительно хотите создать нечто похожее на статические переменные C, просто заключите всю функцию в дополнительный блок и поместите статическую переменную вне функции, но в блоке.
{
my $secret_val = 0;
sub gimme_another {
return ++$secret_val;
}
}
# $secret_val now becomes unreachable by the outside
# world, but retains its value between calls to gimme_another Если эта функция подключается из отдельного файла через require или use, то, вероятно, всё в порядке. Если всё находится в основной программе, вам нужно организовать выполнение my рано, либо поместив весь блок выше основной программы, либо, скорее всего, поместив только BEGIN блок кода вокруг него, чтобы убедиться, что он выполняется до запуска вашей программы:
BEGIN {
my $secret_val = 0;
sub gimme_another {
return ++$secret_val;
}
} Смотрите "BEGIN, UNITCHECK, CHECK, INIT и END" в perlmod о специальных блоках вызываемого кода, BEGIN, UNITCHECK, CHECK, INIT и END.
Если объявлено во внешней области видимости (области видимости файла), то лексические переменные работают примерно как статические переменные файла в С. Они доступны для всех функций в том же файле, объявленных ниже их, но недоступны извне этого файла. Эта стратегия иногда используется в модулях для создания приватных переменных, которые может видеть весь модуль.
Временные Значения с помощью local()
ПРЕДУПРЕЖДЕНИЕ: В общем случае, следует использовать my вместо local, так как это быстрее и безопаснее. Исключение составляют глобальные переменные форматирования, глобальные дескрипторы файлов и форматы, а также непосредственное управление таблицей символов Perl. local используется в основном, когда текущее значение переменной должно быть видно вызываемым подпрограммам.
Синопсис:
# localization of values
local $foo; # make $foo dynamically local
local (@wid, %get); # make list of variables local
local $foo = "flurp"; # make $foo dynamic, and init it
local @oof = @bar; # make @oof dynamic, and init it
local $hash{key} = "val"; # sets a local value for this hash entry
delete local $hash{key}; # delete this entry for the current block
local ($cond ? $v1 : $v2); # several types of lvalues support
# localization
# localization of symbols
local *FH; # localize $FH, @FH, %FH, &FH ...
local *merlyn = *randal; # now $merlyn is really $randal, plus
# @merlyn is really @randal, etc
local *merlyn = 'randal'; # SAME THING: promote 'randal' to *randal
local *merlyn = \$randal; # just alias $merlyn, not @merlyn etc Оператор local изменяет перечисленные переменные, делая их «локальными» для окружающего блока, eval, или do FILE — и для любой подпрограммы, вызываемой изнутри этого блока. Оператор local просто присваивает временные значения глобальным (то есть, относящимся к пакетам) переменным. Он не создает локальную переменную. Это известно как динамическое область действия. Лексическое область действия реализуется с помощью my, что работает примерно как объявление auto в C.
Некоторые типы lvalue также могут быть локализованы: элементы и срезы массивов и хешей, условные операторы (при условии, что их результат всегда может быть локализован), и символические ссылки. Как и в случае с простыми переменными, это создает новые значения с динамическим областью действия.
Если оператору local передаётся более одной переменной или выражения, они должны быть помещены в скобки. Этот оператор работает путём сохранения текущих значений этих переменных в списке аргументов на скрытом стеке и их восстановления при выходе из блока, подпрограммы или eval. Это означает, что вызываемые подпрограммы также могут ссылаться на локальную переменную, но не на глобальную. Список аргументов можно присвоить, если это необходимо, что позволяет вам инициализировать локальные переменные. (Если для конкретной переменной не задан инициализатор, она создается с неопределенным значением.)
Поскольку local является оператором времени выполнения, он выполняется каждый раз при прохождении цикла. Следовательно, более эффективно локализовать переменные за пределами цикла.
Грамматическое примечание к local()
Оператор local просто является модификатором выражения lvalue. При присваивании локализованной переменной local, оператор local не изменяет то, рассматривается ли его список как скаляр или массив. Таким образом
local($foo) = <STDIN>;
local @FOO = <STDIN>; оба обеспечивают контекст списка для правой части, в то время как
local $foo = <STDIN>; обеспечивает скалярный контекст.
Локализация специальных переменных
Если вы локализуете специальную переменную, вы получите новое значение для неё, но её «магия» не исчезнет. Это означает, что все побочные эффекты, связанные с этой «магией», всё ещё работают с локализованным значением.
Эта возможность позволяет работать коду такого вида:
# Read the whole contents of FILE in $slurp
{ local $/ = undef; $slurp = <FILE>; } Обратите, однако, внимание, что это ограничивает локализацию некоторых значений; например, следующее утверждение вызовет ошибку (с версии Perl 5.10.0) с сообщением об ошибке Попытка изменения неизменяемого значения, поскольку переменная $1 является магической и неизменяемой:
local $1 = 2; Исключением является стандартная скалярная переменная: начиная с Perl 5.14, local($_) всегда будет удалять всю «магию» из $_, чтобы сделать возможным безопасное повторное использование $_ в подпрограмме.
ПРЕДУПРЕЖДЕНИЕ: Локализация связанных массивов и хешей в настоящее время не работает должным образом. Это будет исправлено в будущих версиях Perl; в то время следует избегать кода, зависящего от определённого поведения локализации связанных массивов или хешей (локализация отдельных элементов всё ещё допустима). Подробнее см. "Localising Tied Arrays and Hashes Is Broken" в perl58delta.
Локализация глобов
Конструкция
local *name; создает новую запись в таблице символов для глоба name в текущем пакете. Это означает, что все переменные в его слоте глоба ($name, @name, %name, &name и name дескриптор файла) динамически сбрасываются.
Это подразумевает, среди прочего, что любая «магия», которая в конечном итоге содержится в этих переменных, теряется локально. Другими словами, использование local */ не окажет никакого влияния на внутреннее значение разделителя записи.
Локализация элементов составных типов
Также стоит уделить время объяснению того, что происходит, когда вы локализуете элемент составного типа (т. е. элемент массива или хеша). В этом случае элемент локализуется по имени. Это означает, что по окончании области действия локализации сохранённое значение будет восстановлено в элемент хеша, ключ которого был указан в локализации, или в элемент массива, индекс которого был указан в локализации. Если этот элемент был удалён во время действия локализации (например, посредством удаления из хеша или с помощью удаления элемента из массива), он вновь появится, возможно, расширяя массив и заполняя пропущенные элементы значениями по умолчанию undef. Например, если вы напишете
%hash = ( 'This' => 'is', 'a' => 'test' );
@ary = ( 0..5 );
{
local($ary[5]) = 6;
local($hash{'a'}) = 'drill';
while (my $e = pop(@ary)) {
print "$e . . .\n";
last unless $e > 3;
}
if (@ary) {
$hash{'only a'} = 'test';
delete $hash{'a'};
}
}
print join(' ', map { "$_ $hash{$_}" } sort keys %hash),".\n";
print "The array has ",scalar(@ary)," elements: ",
join(', ', map { defined $_ ? $_ : 'undef' } @ary),"\n"; Perl выведет
6 . . .
4 . . .
3 . . .
This is a test only a test.
The array has 6 elements: 0, 1, 2, undef, undef, 5 Поведение оператора local() для несуществующих членов составных типов может быть изменено в будущем.
Локализованное удаление элементов составных типов
Вы можете использовать конструкции delete local $array[$idx] и delete local $hash{key} для удаления записи составного типа для текущего блока и восстановления её при его завершении. Они возвращают значение массива/хеша до локализации, что означает, что они соответственно эквивалентны
do {
my $val = $array[$idx];
local $array[$idx];
delete $array[$idx];
$val
} и
do {
my $val = $hash{key};
local $hash{key};
delete $hash{key};
$val
} за исключением того, что для них локализация ограничена текущим do блоком. Также принимаются срезы.
my %hash = (
a => [ 7, 8, 9 ],
b => 1,
)
{
my $a = delete local $hash{a};
# $a is [ 7, 8, 9 ]
# %hash is (b => 1)
{
my @nums = delete local @$a[0, 2]
# @nums is (7, 9)
# $a is [ undef, 8 ]
$a[0] = 999; # will be erased when the scope ends
}
# $a is back to [ 7, 8, 9 ]
}
# %hash is back to its original state Подпрограммы lvalue
Возможным способом возврата изменяемого значения из подпрограммы является объявление подпрограммы, возвращающей lvalue.
my $val;
sub canmod : lvalue {
$val; # or: return $val;
}
sub nomod {
$val;
}
canmod() = 5; # assigns to $val
nomod() = 5; # ERROR Скалярный/список контекст для подпрограммы и правой части присваивания определяется так, как если бы вызов подпрограммы был заменен скалярным значением. Например, рассмотрим:
data(2,3) = get_data(3,4); В обоих случаях подпрограммы вызываются в скалярном контексте, в то время как в:
(data(2,3)) = get_data(3,4); и в:
(data(2),data(3)) = get_data(3,4); все подпрограммы вызываются в контексте списка.
Подпрограммы lvalue удобны, но следует помнить, что при использовании с объектами они могут нарушать инкапсуляцию. Обычный мутатор может проверить переданный аргумент перед установкой защищаемого атрибута; подпрограмма lvalue не может этого сделать. Если вам требуется специальная обработка при хранении и извлечении значений, рассмотрите использование модуля Sentinel CPAN или что-то подобное.
Лексические подпрограммы
Начиная с Perl 5.18, вы можете объявить частную подпрограмму с помощью my или state. Как и в случае с переменными состояния, ключевое слово state доступно только с версии use feature 'state' или use 5.010 или выше.
До Perl 5.26 лексические подпрограммы считались экспериментальными и были доступны только с помощью use feature 'lexical_subs' псевдонима. Они также вызывали предупреждение, если категория предупреждений "experimental::lexical_subs" не была отключена.
Эти подпрограммы видны только внутри блока, в котором они объявлены, и только после этого объявления:
# Include these two lines if your code is intended to run under Perl
# versions earlier than 5.26.
no warnings "experimental::lexical_subs";
use feature 'lexical_subs';
foo(); # calls the package/global subroutine
state sub foo {
foo(); # also calls the package subroutine
}
foo(); # calls "state" sub
my $ref = \&foo; # take a reference to "state" sub
my sub bar { ... }
bar(); # calls "my" sub Нельзя (непосредственно) написать рекурсивную лексическую подпрограмму:
# WRONG
my sub baz {
baz();
} Этот пример терпит неудачу, поскольку baz() ссылается на подпрограмму пакета/глобальную подпрограмму baz, а не на лексическую подпрограмму, которая в данный момент определяется.
Решение заключается в использовании __SUB__:
my sub baz {
__SUB__->(); # calls itself
} Можно предварительно объявить лексическую подпрограмму. Синтаксис определения подпрограммы sub foo {...} учитывает любое предыдущее объявление my sub; или state sub;. Однако использование этого для определения рекурсивных подпрограмм — плохая идея:
my sub baz; # predeclaration
sub baz { # define the "my" sub
baz(); # WRONG: calls itself, but leaks memory
} Как и my $f; $f = sub { $f->() }, этот пример приводит к утечке памяти. Имя baz является ссылкой на подпрограмму, а подпрограмма использует имя baz; они поддерживают друг друга (см. "Circular References" в perlref).
state sub против my sub
В чём разница между подпрограммами "state" и "my"? Каждый раз, когда выполнение входит в блок с объявлением подпрограмм "my", создается новая копия каждой подпрограммы. Подпрограммы "state" сохраняются от одного выполнения содержащего блока к другому.
Таким образом, в общем случае подпрограммы "state" быстрее. Но подпрограммы "my" необходимы, если вы хотите создать замыкания:
sub whatever {
my $x = shift;
my sub inner {
... do something with $x ...
}
inner();
} В этом примере новая подпрограмма $x создается при вызове whatever, и также создаётся новая подпрограмма inner, которая может видеть новую подпрограмму $x. Подпрограмма "state" увидит только $x из первого вызова подпрограммы whatever.
our подпрограммы
Как и our $variable, our sub создает лексический псевдоним для подпрограммы пакета с тем же именем.
Два основных применения: переключение обратно на использование подпрограммы пакета внутри внутреннего блока:
sub foo { ... }
sub bar {
my sub foo { ... }
{
# need to use the outer foo here
our sub foo;
foo();
}
} и предоставление подпрограмме видимости в других пакетах в том же блоке:
package MySneakyModule;
our sub do_something { ... }
sub do_something_with_caller {
package DB;
() = caller 1; # sets @DB::args
do_something(@args); # uses MySneakyModule::do_something
} Передача записей таблицы символов (typeglobs)
ПРЕДУПРЕЖДЕНИЕ: Механизм, описанный в этом разделе, изначально был единственным способом моделирования передачи по ссылке в более ранних версиях Perl. Хотя он всё ещё работает в современных версиях, новый механизм ссылок, как правило, проще в использовании. См. ниже.
Иногда вам не нужно передавать значение массива в подпрограмму, а нужно передать его имя, чтобы подпрограмма могла изменить глобальную копию массива вместо работы с локальной копией. В Perl вы можете ссылаться на все объекты с определённым именем, добавив звездочку перед именем: *foo. Это часто называется «typeglob», потому что звездочка в начале может рассматриваться как подстановочный символ для всех специфических префиксов переменных, подпрограмм и т. п.
При вычислении typeglob возвращает скалярное значение, представляющее все объекты с этим именем, включая любые дескрипторы файлов, форматы или подпрограммы. При присваивании оно заставляет имя ссылаться на то, чему было присвоено значение *.
sub doubleary {
local(*someary) = @_;
foreach $elem (@someary) {
$elem *= 2;
}
}
doubleary(*foo);
doubleary(*bar); Скаляры уже передаются по ссылке, поэтому вы можете изменять скалярные аргументы без использования этого механизма, явно ссылаясь на $_[0] и т. д. Вы можете изменить все элементы массива, передавая все элементы как скаляры, но для изменения размера массива или добавления/удаления элементов необходимо использовать механизм * (или эквивалентный механизм ссылок). Вероятно, передача typeglob (или ссылки) будет быстрее.
Даже если вы не хотите изменять массив, этот механизм полезен для передачи нескольких массивов в одном списке, потому что обычно механизм списка объединит все значения массива, так что вы не сможете извлечь отдельные массивы. Более подробную информацию о типах глобальных переменных см. в разделе "Typeglobs and Filehandles" в perldata.
Когда всё ещё использовать local()
Несмотря на существование my, есть ещё три места, где оператор local всё ещё превосходит. Фактически, в этих трёх местах вы обязательно должны использовать local вместо my.
-
Вам нужно присвоить глобальной переменной временное значение, особенно переменной $_.
Глобальные переменные, такие как
@ARGVили переменные пунктуации, должны бытьlocalзированы с помощьюlocal(). Этот блок считывает /etc/motd и разбивает его на части, разделённые строками с одинаковыми символами, которые помещаются в@Fields.{ local @ARGV = ("/etc/motd"); local $/ = undef; local $_ = <>; @Fields = split /^\s*=+\s*$/; }В частности, важно
localзировать $_ в любой процедуре, которая присваивает ей значение. Обращайте внимание на неявные присваивания вwhileусловных операторах. -
Вам нужно создать локальную дескриптор файла или каталога или локальную функцию.
Функция, которая нуждается в собственном дескрипторе файла, должна использовать
local()для целого типа глобальной переменной. Это можно использовать для создания новых записей в таблице символов:sub ioqueue { local (*READER, *WRITER); # not my! pipe (READER, WRITER) or die "pipe: $!"; return (*READER, *WRITER); } ($head, $tail) = ioqueue();См. модуль Symbol, чтобы узнать, как создать анонимные записи в таблице символов.
Поскольку присвоение ссылки на тип глобальной переменной создаёт псевдоним, это можно использовать для создания фактически локальной функции или, по крайней мере, локального псевдонима.
{ local *grow = \&shrink; # only until this block exits grow(); # really calls shrink() move(); # if move() grow()s, it shrink()s too } grow(); # get the real grow() againСм. "Шаблоны функций" в perlref для получения более подробной информации о манипулировании функциями по имени таким способом.
-
Вы хотите временно изменить только один элемент массива или хэша.
Вы можете
localзировать только один элемент агрегата. Обычно это делается для динамических элементов:{ local $SIG{INT} = 'IGNORE'; funct(); # uninterruptible } # interruptibility automatically restored hereНо это также работает с лексически объявленными агрегатами.
Передача по ссылке
Если вы хотите передать в функцию более одного массива или хэша — или вернуть их из неё — и сохранить их целостность, вам придётся использовать явную передачу по ссылке. Прежде чем делать это, вам необходимо понять ссылки, как подробно описано в perlref. В противном случае этот раздел может вам показаться неясным.
Вот несколько простых примеров. Сначала давайте передадим несколько массивов в функцию и заставим её pop все их, вернув новый список всех их предыдущих последних элементов:
@tailings = popmany ( \@a, \@b, \@c, \@d );
sub popmany {
my $aref;
my @retlist;
foreach $aref ( @_ ) {
push @retlist, pop @$aref;
}
return @retlist;
} Вот как вы можете написать функцию, которая возвращает список ключей, встречающихся во всех хэшах, переданных ей:
@common = inter( \%foo, \%bar, \%joe );
sub inter {
my ($k, $href, %seen); # locals
foreach $href (@_) {
while ( $k = each %$href ) {
$seen{$k}++;
}
}
return grep { $seen{$_} == @_ } keys %seen;
} До сих пор мы используем только обычный механизм возврата списка. Что произойдёт, если вы захотите передать или вернуть хэш? Ну, если вы используете только один из них, или вам всё равно, что они конкатенируются, то обычная схема вызова допустима, хотя и немного дорогостоящая.
Где люди сталкиваются с проблемами:
(@a, @b) = func(@c, @d);
or
(%a, %b) = func(%c, %d); Эта синтаксическая конструкция просто не сработает. Она устанавливает только @a или %a и очищает @b или %b. Кроме того, функции не были переданы в два отдельных массива или хэша: она получила один длинный список в @_, как всегда.
Если вы можете организовать для всех обработку этого через ссылки, код будет чище, хотя и не так приятен на вид. Вот функция, которая принимает два массива ссылок в качестве аргументов, возвращая два элемента массива в порядке количества элементов в них:
($aref, $bref) = func(\@c, \@d);
print "@$aref has more than @$bref\n";
sub func {
my ($cref, $dref) = @_;
if (@$cref > @$dref) {
return ($cref, $dref);
} else {
return ($dref, $cref);
}
} Оказывается, вы можете сделать это также:
(*a, *b) = func(\@c, \@d);
print "@a has more than @b\n";
sub func {
local (*c, *d) = @_;
if (@c > @d) {
return (\@c, \@d);
} else {
return (\@d, \@c);
}
} Здесь мы используем типы глобальных переменных для выполнения алиасирования таблицы символов. Однако это немного тонко, и это также не сработает, если вы используете my переменные, потому что только глобальные переменные (даже в виде маскировки как localы) находятся в таблице символов.
Если вы передаёте дескрипторы файлов, вы обычно можете просто использовать обычный тип глобальной переменной, как *STDOUT, но ссылки на типы глобальных переменных тоже работают. Например:
splutter(\*STDOUT);
sub splutter {
my $fh = shift;
print $fh "her um well a hmmm\n";
}
$rec = get_rec(\*STDIN);
sub get_rec {
my $fh = shift;
return scalar <$fh>;
} Если вы планируете генерировать новые дескрипторы файлов, вы можете сделать это. Обратите внимание, что вы должны передавать только *FH, а не его ссылку.
sub openit {
my $path = shift;
local *FH;
return open (FH, $path) ? *FH : undef;
} Прототипы
Perl поддерживает очень ограниченный вид проверки аргументов во время компиляции с помощью прототипов функций. Это может быть объявлено в разделе PROTO или с помощью атрибута прототипа атрибута прототипа. Если вы объявите любой из
sub mypush (\@@)
sub mypush :prototype(\@@) тогда mypush() принимает аргументы точно так же, как push().
Если подписи подпрограмм включены (см. "Подписи"), то более короткий синтаксис PROTO недоступен, потому что он будет конфликтовать с подписями. В этом случае прототип может быть объявлен только в виде атрибута.
Объявление функции должно быть видно во время компиляции. Прототип влияет только на интерпретацию вызовов функций нового стиля, где новый стиль определён как не использующий символ &. Другими словами, если вы вызываете её как встроенную функцию, то она ведёт себя как встроенная функция. Если вы вызываете её как подпрограмму старого стиля, то она ведёт себя как подпрограмма старого стиля. Из этого правила естественным образом следует, что прототипы не влияют на ссылки на подпрограммы, такие как \&foo или на косвенные вызовы подпрограмм, такие как &{$subref} или $subref->().
Вызовы методов также не подвергаются влиянию прототипов, потому что функция, которая будет вызвана, неопределена во время компиляции, так как точный код, который будет вызван, зависит от наследования.
Поскольку цель этой функции в первую очередь заключается в том, чтобы позволить вам определить подпрограммы, которые работают как встроенные функции, вот прототипы для некоторых других функций, которые анализируются почти точно так же, как соответствующие встроенные.
Declared as Called as
sub mylink ($$) mylink $old, $new
sub myvec ($$$) myvec $var, $offset, 1
sub myindex ($$;$) myindex &getstring, "substr"
sub mysyswrite ($$$;$) mysyswrite $buf, 0, length($buf) - $off, $off
sub myreverse (@) myreverse $a, $b, $c
sub myjoin ($@) myjoin ":", $a, $b, $c
sub mypop (\@) mypop @array
sub mysplice (\@$$@) mysplice @array, 0, 2, @pushme
sub mykeys (\[%@]) mykeys %{$hashref}
sub myopen (*;$) myopen HANDLE, $name
sub mypipe (**) mypipe READHANDLE, WRITEHANDLE
sub mygrep (&@) mygrep { /foo/ } $a, $b, $c
sub myrand (;$) myrand 42
sub mytime () mytime Любой обратный слэш в прототипе представляет собой фактический аргумент, который должен начинаться с этого символа (при необходимости предваряемый my, our или local), за исключением $, который будет принимать любое выражение скалярного значения lvalue, такое как $foo = 7 или my_function()->[0]. Значение, передаваемое как часть @_ будет ссылкой на фактический аргумент, указанный в вызове подпрограммы, полученный путём применения \ к этому аргументу.
Вы можете использовать обозначение группы обратного слэша \[] для указания более чем одного допустимого типа аргумента. Например:
sub myref (\[$@%&*]) позволит вызывать myref() как
myref $var
myref @array
myref %hash
myref &sub
myref *glob и первый аргумент myref() будет ссылкой на скаляр, массив, хэш, код или глобальную переменную.
У необращённых с обратным слэшем символов прототипов есть специальные значения. Любой необращённый с обратным слэшем @ или % поглощает все оставшиеся аргументы и принуждает контекст списка. Аргумент, представленный $ принуждает контекст скаляра. & требует анонимной подпрограммы, которая, если она передаётся в качестве первого аргумента, не требует ключевого слова sub или последующей запятой.
* позволяет подпрограмме принимать в этом месте неприводимое слово, константу, скалярное выражение, глобальную переменную или ссылку на глобальную переменную. Значение будет доступно подпрограмме либо как простой скаляр, либо (в последних двух случаях) как ссылка на глобальную переменную. Если вы хотите всегда преобразовывать такие аргументы в ссылку на глобальную переменную, используйте Symbol::qualify_to_ref() следующим образом:
use Symbol 'qualify_to_ref';
sub foo (*) {
my $fh = qualify_to_ref(shift, caller);
...
} Прототип + — это специальная альтернатива $, который будет действовать как \[@%] при указании буквальной переменной массива или хэша, но в остальных случаях будет принуждать скалярный контекст аргумента. Это полезно для функций, которые должны принимать либо буквальный массив, либо ссылку на массив в качестве аргумента:
sub mypush (+@) {
my $aref = shift;
die "Not an array or arrayref" unless ref $aref eq 'ARRAY';
push @$aref, @_;
} При использовании прототипа + ваша функция должна проверять, что аргумент является допустимого типа.
Точка с запятой (;) отделяет обязательные аргументы от необязательных аргументов. Она избыточна перед @ или %, которые поглощают всё остальное.
В качестве последнего символа прототипа или непосредственно перед точкой с запятой, а также перед @ или %, вы можете использовать _ вместо $: если этот аргумент не указан, то вместо него будет использоваться $_.
Обратите внимание, как три последних примера в таблице выше обрабатываются парсером особо. mygrep() анализируется как истинный оператор списка, myrand() анализируется как истинный унарный оператор с унарным приоритетом, равным rand(), и mytime() действительно без аргументов, как и time(). То есть, если вы скажете
mytime +2; вы получите mytime() + 2, а не mytime(2), как это было бы проанализировано без прототипа. Если вы хотите принудительно сделать унарную функцию с тем же приоритетом, что и оператор списка, добавьте ; в конец прототипа:
sub mygetprotobynumber($;);
mygetprotobynumber $a > $b; # parsed as mygetprotobynumber($a > $b) Интересно, что с помощью & вы можете генерировать новый синтаксис, при условии, что он находится в начальном положении:
sub try (&@) {
my($try,$catch) = @_;
eval { &$try };
if ($@) {
local $_ = $@;
&$catch;
}
}
sub catch (&) { $_[0] }
try {
die "phooey";
} catch {
/phooey/ and print "unphooey\n";
}; Это выводит "unphooey". (Да, всё ещё есть нерешённые вопросы, связанные с видимостью @_. Я игнорирую этот вопрос на данный момент. (Но обратите внимание, что если мы сделаем @_ лексически ограниченным, эти анонимные подпрограммы могут действовать как замыкания... (Ой, это звучит немного по-лисповски? (Неважно.))))
А вот повторная реализация оператора Perl grep:
sub mygrep (&@) {
my $code = shift;
my @result;
foreach $_ (@_) {
push(@result, $_) if &$code;
}
@result;
} Некоторые люди предпочитали бы полноалфавитные прототипы. Алфавитно-цифровые прототипы намеренно исключались из прототипов, чтобы в будущем добавить именованные формальные параметры. Основная цель текущего механизма — позволить авторам модулей предоставлять лучшие диагностические сообщения для пользователей модулей. Ларри считает эту запись довольно понятной для программистов Perl и что она не будет сильно мешать основному коду модуля, а также не усложнит его чтение. Визуальный шум заключён в небольшой капсуле, которую легко проглотить.
Если вы попытаетесь использовать алфавитно-цифровую последовательность в прототипе, вы сгенерируете необязательное предупреждение - «Недопустимый символ в прототипе…». К сожалению, более ранние версии Perl разрешали использование прототипа, пока его префикс был допустимым прототипом. Предупреждение может быть повышено до фатальной ошибки в будущей версии Perl, как только будет исправлена большая часть проблемного кода.
Наверное, лучше прототипировать новые функции, а не дорабатывать прототипы в более старых. Это связано с тем, что вам нужно быть особенно осторожными относительно беззвучных требований к разным контекстам списка и скаляра. Например, если вы решите, что функция должна принимать только один параметр, как это:
sub func ($) {
my $n = shift;
print "you gave me $n\n";
} и кто-то вызвал его с массивом или выражением, возвращающим список:
func(@foo);
func( $text =~ /\w+/g ); Тогда вы просто предоставили автоматический scalar перед их аргументом, что может быть довольно неожиданно. Старый @foo, который раньше хранил одну вещь, не передаётся. Вместо этого func() теперь передаётся в 1; то есть, количество элементов в @foo. И m//g вызывается в скалярном контексте, поэтому вместо списка слов он возвращает булево значение и продвигает pos($text). Ой!
Если подпрограмма имеет как PROTO, так и BLOCK, прототип не применяется до тех пор, пока BLOCK не будет полностью определён. Это означает, что рекурсивной функции с прототипом нужно предварительно объявить прототип, чтобы он вступил в силу, как в этом примере:
sub foo($$);
sub foo($$) {
foo 1, 2;
} Конечно, всё это очень мощно и должно использоваться только с осторожностью, чтобы сделать мир лучше.
Функции-константы
Функции с прототипом () являются потенциальными кандидатами для встраивания. Если результат после оптимизации и сворачивания констант равен либо константе, либо скаляру с лексической областью видимости, у которого нет других ссылок, то он будет использован вместо вызовов функций, сделанных без &. Вызовы, сделанные с использованием &, никогда не встраиваются. (См. constant.pm для простого способа объявления большинства констант.)
Следующие функции будут все встроены:
sub pi () { 3.14159 } # Not exact, but close.
sub PI () { 4 * atan2 1, 1 } # As good as it gets,
# and it's inlined, too!
sub ST_DEV () { 0 }
sub ST_INO () { 1 }
sub FLAG_FOO () { 1 << 8 }
sub FLAG_BAR () { 1 << 9 }
sub FLAG_MASK () { FLAG_FOO | FLAG_BAR }
sub OPT_BAZ () { not (0x1B58 & FLAG_MASK) }
sub N () { int(OPT_BAZ) / 3 }
sub FOO_SET () { 1 if FLAG_MASK & FLAG_FOO }
sub FOO_SET2 () { if (FLAG_MASK & FLAG_FOO) { 1 } } (Обратите внимание, что в Perl 5.20 и ранее последний пример не всегда встраивался, так как поведение с подпрограммами, содержащими внутренние области видимости, не было последовательным.) Вы можете противодействовать встраиванию, используя явное return:
sub baz_val () {
if (OPT_BAZ) {
return 23;
}
else {
return 42;
}
}
sub bonk_val () { return 12345 } Как упоминалось ранее, вы также можете динамически объявлять встраиваемые подпрограммы во время BEGIN, если их тело состоит из скаляра с лексической областью видимости, у которого нет других ссылок. Только первый пример здесь будет встроен:
BEGIN {
my $var = 1;
no strict 'refs';
*INLINED = sub () { $var };
}
BEGIN {
my $var = 1;
my $ref = \$var;
no strict 'refs';
*NOT_INLINED = sub () { $var };
} Не очень очевидное замечание по этому поводу (см. [RT #79908]) заключается в том, что переменная будет немедленно встроена и перестанет вести себя как обычная лексическая переменная, например, это напечатает 79907, а не 79908:
BEGIN {
my $x = 79907;
*RT_79908 = sub () { $x };
$x++;
}
print RT_79908(); # prints 79907 Начиная с Perl 5.22, это ошибочное поведение, хотя и сохраняется для обратной совместимости, выявляется и генерирует предупреждение о устаревании. Если вы хотите, чтобы подпрограмма была встроена (без предупреждения), убедитесь, что переменная не используется в контексте, где она может быть изменена помимо места её объявления.
# Fine, no warning
BEGIN {
my $x = 54321;
*INLINED = sub () { $x };
}
# Warns. Future Perl versions will stop inlining it.
BEGIN {
my $x;
$x = 54321;
*ALSO_INLINED = sub () { $x };
} Perl 5.22 также добавляет экспериментальное атрибут «const» в качестве альтернативы. (Отключите предупреждения «experimental::const_attr», если вы хотите его использовать.) При применении к анонимной подпрограмме, она заставляет подпрограмму быть вызванной при вычислении sub выражения. Возвращаемое значение сохраняется и преобразуется в константную подпрограмму:
my $x = 54321;
*INLINED = sub : const { $x };
$x++; Возвращаемое значение INLINED в этом примере всегда будет 54321, независимо от последующих изменений $x. Вы также можете поместить любой произвольный код внутрь подпрограммы, так как он будет выполнен немедленно, а его возвращаемое значение будет сохранено аналогичным образом.
Если вы действительно хотите подпрограмму с прототипом () , которая возвращает лексическую переменную, вы легко можете заставить её не встраиваться, добавив явное return:
BEGIN {
my $x = 79907;
*RT_79908 = sub () { return $x };
$x++;
}
print RT_79908(); # prints 79908 Самый простой способ определить, была ли подпрограмма встроена, — использовать B::Deparse. Рассмотрим этот пример двух подпрограмм, возвращающих 1, одна с прототипом () , что приводит к её встраиванию, а другая без него (вывод deparse усечён для наглядности):
$ perl -MO=Deparse -le 'sub ONE { 1 } if (ONE) { print ONE if ONE }'
sub ONE {
1;
}
if (ONE ) {
print ONE() if ONE ;
}
$ perl -MO=Deparse -le 'sub ONE () { 1 } if (ONE) { print ONE if ONE }'
sub ONE () { 1 }
do {
print 1
}; Если вы переопределите подпрограмму, которая подходила для встраивания, вы получите предупреждение по умолчанию. Вы можете использовать это предупреждение, чтобы узнать, считается ли конкретная подпрограмма встраиваемой, так как оно отличается от предупреждения о переопределении невстраиваемых подпрограмм:
$ perl -e 'sub one () {1} sub one () {2}'
Constant subroutine one redefined at -e line 1.
$ perl -we 'sub one {1} sub one {2}'
Subroutine one redefined at -e line 1. Предупреждение считается достаточно серьёзным, чтобы не влиять на переключатель -w (или его отсутствие), так как ранее скомпилированные вызовы функции всё ещё будут использовать старое значение функции. Если вам нужно переопределить подпрограмму, вам нужно убедиться, что она не встроена, либо путём удаления прототипа () (что изменяет семантику вызова, поэтому будьте осторожны), либо путём предотвращения механизма встраивания каким-либо другим способом, например, добавив явное return , как упоминалось выше:
sub not_inlined () { return 23 } Переопределение встроенных функций
Многие встроенные функции могут быть переопределены, хотя это следует делать лишь изредка и по уважительной причине. Обычно это может быть сделано пакетом, пытающимся эмулировать отсутствующую встроенную функциональность на не-Unix-системе.
Переопределение может быть выполнено только импортом имени из модуля во время компиляции — обычного предварительного объявления недостаточно. Однако, pragma use subs позволяет, по сути, предварительно объявлять подпрограммы с помощью синтаксиса импорта, и эти имена могут затем переопределять встроенные:
use subs 'chdir', 'chroot', 'chmod', 'chown';
chdir $somewhere;
sub chdir { ... } Чтобы однозначно сослаться на встроенную форму, предваряйте встроенное имя специальным квалификатором пакета CORE::. Например, запись CORE::open() всегда ссылается на встроенную open(), даже если текущий пакет импортировал какую-то другую подпрограмму под названием &open() из другого места. Несмотря на то, что это выглядит как обычный вызов функции, это не так: префикс CORE:: в этом случае является частью синтаксиса Perl и работает для любого ключевого слова, независимо от содержимого пакета CORE. Получение ссылки на него, т. е. \&CORE::open, работает только для некоторых ключевых слов. См. CORE.
Модули библиотек вообще не должны экспортировать встроенные имена, такие как open или chdir в качестве части их списка по умолчанию @EXPORT, потому что они могут незаметно попасть в пространство имён другого человека и неожиданно изменить семантику. Вместо этого, если модуль добавляет это имя в @EXPORT_OK, то пользователь может импортировать это имя явно, но не неявно. То есть, они могут сказать
use Module 'open'; и это импортирует open переопределение. Но если они скажут
use Module; они получат стандартные импорты без переопределений.
Предыдущий механизм переопределения встроенного ограничен, вполне преднамеренно, пакетом, который запрашивает импорт. Существует второй метод, который иногда применим, когда вы хотите переопределить встроенную часть повсюду, не заботясь о границах имён пространства. Это достигается путём импорта подпрограммы в специальное пространство имён CORE::GLOBAL::. Вот пример, который довольно бесцеремонно заменяет оператор glob чем-то, что понимает регулярные выражения.
package REGlob;
require Exporter;
@ISA = 'Exporter';
@EXPORT_OK = 'glob';
sub import {
my $pkg = shift;
return unless @_;
my $sym = shift;
my $where = ($sym =~ s/^GLOBAL_// ? 'CORE::GLOBAL' : caller(0));
$pkg->export($where, $sym, @_);
}
sub glob {
my $pat = shift;
my @got;
if (opendir my $d, '.') {
@got = grep /$pat/, readdir $d;
closedir $d;
}
return @got;
}
1; И вот как это можно (злоупотребить):
#use REGlob 'GLOBAL_glob'; # override glob() in ALL namespaces
package Foo;
use REGlob 'glob'; # override glob() in Foo:: only
print for <^[a-z_]+\.pm\$>; # show all pragmatic modules Исходный комментарий показывает искусственный, даже опасный пример. Переопределив glob глобально, вы бы принудительно навязали новое (и подрывное) поведение оператора glob для каждого пространства имён, без полного осознания или сотрудничества модулей, которые владеют этими пространствами имён. Естественно, это следует делать с крайней осторожностью — если вообще необходимо.
Пример REGlob выше не реализует всю необходимую поддержку для чистого переопределения оператора glob Perl. Встроенный glob имеет различное поведение в зависимости от того, появляется ли он в скалярном или списковом контексте, но наш REGlob не имеет. Действительно, у многих встроенных функций Perl есть такое контекстно-зависимое поведение, и это поведение должно быть надлежащим образом поддержано надлежаще написанным переопределением. Для полного функционального примера переопределения glob изучите реализацию File::DosGlob в стандартной библиотеке.
При переопределении встроенной функции ваше замещение должно быть согласованным (если это возможно) с исходным встроенным синтаксисом. Вы можете этого добиться, используя подходящий прототип. Чтобы получить прототип переопределяемой встроенной функции, используйте функцию prototype с аргументом "CORE::builtin_name" (см. "prototype" в perlfunc).
Однако следует отметить, что некоторые встроенные функции не могут иметь свой синтаксис, выраженный прототипом (например, system или chomp). Если вы их переопределите, вы не сможете полностью имитировать их исходный синтаксис.
Встроенные функции do, require и glob также могут быть переопределены, но из-за специальной магии их исходный синтаксис сохраняется, и вам не нужно определять прототип для их заменителей. (Вы не можете переопределить синтаксис do BLOCK).
require имеет специальные дополнительные тёмные магические свойства: если вы вызываете свой require заменитель как require Foo::Bar, он фактически получит аргумент "Foo/Bar.pm" в @_. См. "require" в perlfunc.
И, как вы заметили из предыдущего примера, если вы переопределите glob, оператор подстановки <*> также переопределяется.
Аналогичным образом, переопределение функции readline также переопределяет эквивалентный оператор ввода-вывода <FILEHANDLE>. Кроме того, переопределение readpipe также переопределяет операторы `` и qx//.
Наконец, некоторые встроенные функции (например, exists или grep ) не могут быть переопределены.
Автозагрузка
Если вы вызываете неопределённую подпрограмму, вы обычно получите немедленную ошибку, сообщая о том, что подпрограмма не существует. (То же самое относится к подпрограммам, используемым в качестве методов, когда метод не существует в каком-либо базовом классе пакета класса.) Однако, если AUTOLOAD подпрограмма определена в пакете или пакетах, используемых для поиска исходной подпрограммы, тогда эта AUTOLOAD подпрограмма вызывается с аргументами, которые должны были быть переданы исходной подпрограмме. Полное полное имя исходной подпрограммы магическим образом появляется в глобальной переменной $AUTOLOAD того же пакета, что и AUTOLOAD подпрограмма. Имя не передаётся в качестве обычного аргумента, потому что, э, ну, просто так, вот почему. (В качестве исключения, вызов метода для несуществующего import или unimport метода просто пропускается. Кроме того, если подпрограмма AUTOLOAD — это XSUB, есть и другие способы получения имени подпрограммы. См. ""Автозагрузка с XSUB" в perlguts для получения дополнительной информации.)
Многие AUTOLOAD процедуры загружают определение запрошенной подпрограммы с помощью eval(), затем выполняют эту подпрограмму с помощью специальной формы goto(), которая стирает кадр стека AUTOLOAD процедуры без следа. (См. исходный код стандартного модуля, документированного в AutoLoader, например.) Но AUTOLOAD подпрограмма также может просто эмулировать подпрограмму и никогда её не определять. Например, предположим, что функция, которая не была определена, должна просто вызвать system с этими аргументами. Всё, что вам нужно сделать, это:
sub AUTOLOAD {
my $program = $AUTOLOAD;
$program =~ s/.*:://;
system($program, @_);
}
date();
who('am', 'i');
ls('-l'); На самом деле, если вы предварительно объявляете функции, которые хотите вызывать таким образом, вам даже не нужны скобки:
use subs qw(date who ls);
date;
who "am", "i";
ls '-l'; Более полный пример этого — модуль Shell на CPAN, который может обрабатывать вызовы неопределённых подпрограмм как вызовы внешних программ.
Доступны механизмы, которые помогут авторам модулей разделить их модули на загружаемые по мере необходимости файлы. См. стандартный модуль AutoLoader, описанный в AutoLoader, и в AutoSplit, стандартные модули SelfLoader в SelfLoader и документ о добавлении C-функций в код Perl в perlxs.
Атрибуты подпрограмм
Объявление или определение подпрограммы может иметь список атрибутов, связанных с ней. Если такой список атрибутов присутствует, он разбивается по пробелам или двоеточиям и обрабатывается так, как будто была видна use attributes. См. attributes для получения подробностей о поддерживаемых в настоящее время атрибутах. В отличие от ограничения устаревшего use attrs, синтаксис sub : ATTRLIST работает для связывания атрибутов с предварительным объявлением, а не только с определением подпрограммы.
Атрибуты должны быть допустимыми именами простых идентификаторов (без каких-либо знаков препинания, кроме символа «_»). К ним может быть добавлен список параметров, который проверяется только на правильность вложенности скобок ('(',')').
Примеры правильного синтаксиса (даже если атрибуты неизвестны):
sub fnord (&\%) : switch(10,foo(7,3)) : expensive;
sub plugh () : Ugly('\(") :Bad;
sub xyzzy : _5x5 { ... } Примеры неправильного синтаксиса:
sub fnord : switch(10,foo(); # ()-string not balanced
sub snoid : Ugly('('); # ()-string not balanced
sub xyzzy : 5x5; # "5x5" not a valid identifier
sub plugh : Y2::north; # "Y2::north" not a simple identifier
sub snurt : foo + bar; # "+" not a colon or space Список атрибутов передаётся как список константных строк в код, который связывает их с подпрограммой. В частности, второй пример правильного синтаксиса выше в настоящее время выглядит так с точки зрения обработки и вызова:
use attributes __PACKAGE__, \&plugh, q[Ugly('\(")], 'Bad'; Для получения более подробной информации о списках атрибутов и их обработке см. attributes и Attribute::Handlers.
См. также
См. "Шаблоны функций" в perlref для получения дополнительной информации о ссылках и замыканиях. См. perlxs, если вы хотите узнать о вызове C-подпрограмм из Perl. См. perlembed, если вы хотите узнать о вызове Perl-подпрограмм из C. См. perlmod, чтобы узнать о группировке функций в отдельных файлах. См. perlmodlib, чтобы узнать, какие модули библиотеки поставляются в стандартной комплектации вашей системы. См. perlootut, чтобы узнать, как выполнять вызовы методов объектов.
© 1993–2020 Larry Wall and others
Licensed under the GNU General Public License version 1 or later, or the Artistic License.
The Perl logo is a trademark of the Perl Foundation.
https://perldoc.perl.org/5.28.3/perlsub