perlsub
СОДЕРЖАНИЕ
- ИМЯ
- СИНТАКСИС
- ОПИСАНИЕ
- Подписи
- Приватные переменные через my()
- Постоянные приватные переменные
- Временные значения через local()
- Подпрограммы lvalue
- Лексические подпрограммы
- Передача записей таблицы символов (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 или анонимных подпрограмм. Вы даже можете вызвать функцию косвенно, используя переменную, содержащую её имя, или ссылку на CODE.
Модель 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(@_) !!
use strict 'subs';
foo; # like foo() iff sub foo predeclared, else
# a compile-time error
no strict 'subs';
foo; # like foo() iff sub foo predeclared, else
# a literal string "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;
} Необязательные позиционные параметры должны следовать за всеми обязательными позиционными параметрами. (Если нет обязательных позиционных параметров, то необязательные позиционные параметры могут быть первыми в подписи.) Если имеется несколько необязательных позиционных параметров и количество переданных аргументов недостаточно для заполнения всех из них, они будут заполняться слева направо.
После позиционных параметров дополнительные аргументы могут быть捕获 в параметре сортировки. Самый простой вид — это просто массив переменных:
sub foo ($filter, @inputs) {
print $filter->($_) foreach @inputs;
} С параметром сортировки в подписи нет ограничений на количество передаваемых аргументов. Параметр массива сортировки может быть безымянным, как и позиционный параметр, в этом случае его единственное действие заключается в отключении ограничения аргументов, которое в противном случае применяется:
sub foo ($thing, @) {
print $thing;
} Вместо этого параметр сортировки может быть хэш-таблицей, в этом случае аргументы, доступные для него, интерпретируются как чередующиеся ключи и значения. Должно быть столько же ключей, сколько и значений: если количество аргументов нечетное, будет выброшено исключение. Ключи будут преобразованы в строки, и если есть дубликаты, то последний экземпляр имеет приоритет над предыдущим, как и при стандартном построении хэш-таблицы.
sub foo ($filter, %inputs) {
print $filter->($_, $inputs{$_}) foreach sort keys %inputs;
} Параметр хэш-таблицы сортировки может быть безымянным, как и другие типы параметров. Он все равно требует, чтобы количество аргументов, доступных ему, было четным, даже если они не записываются в переменную.
sub foo ($thing, %) {
print $thing;
} Параметр сортировки, массив или хэш-таблица, должен быть последним элементом в подписи. Он может следовать за обязательными и необязательными позиционными параметрами; он также может быть единственным элементом в подписи. Параметры сортировки не могут иметь значений по умолчанию: если для них не переданы аргументы, вы получите пустой массив или пустую хэш-таблицу.
Подпись может быть полностью пустой, в этом случае она только проверяет, что вызывающая сторона не передала аргументов:
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 всё ещё находится в стадии развития. Текущая семантика и интерфейс могут измениться. См. атрибуты и Attribute::Handlers.
Оператор my объявляет перечисленные переменные, ограниченные лексически, внутри окружающего блока, условного (if/unless/elsif/else), цикла (for/foreach/while/until/continue), подпрограммы, eval, или do/require/use файла. Если указано более одного значения, список должен быть помещён в скобки. Все перечисленные элементы должны быть допустимыми lvalues. Лексически ограничены только буквенно-цифровые идентификаторы — магические встроенные функции, такие как $/, в настоящее время должны быть localзированы с помощью local.
В отличие от динамических переменных, созданных оператором local, лексические переменные, объявленные с помощью my, полностью скрыты от внешнего мира, включая любые вызываемые подпрограммы. Это верно, если это та же самая подпрограмма, вызываемая из себя или из другого места — каждый вызов получает свою собственную копию.
Это не означает, что переменная my объявленная в статически окружающем лексическом пространстве, будет невидимой. Защищаются только динамические области видимости. Например, функция bumpx() ниже имеет доступ к лексической переменной $x, потому что как my, так и sub произошли в одной области видимости, предположительно, области видимости файла.
my $x = 10;
sub bumpx { $x++ } Однако eval() может видеть лексические переменные области видимости, в которой она оценивается, при условии, что имена не скрыты объявлениями внутри eval() самой. См. perlref.
Список параметров функции my() можно присвоить, если нужно, что позволяет инициализировать ваши переменные. (Если для конкретной переменной нет инициализатора, она создаётся со значением undef.) Обычно это используется для именования входных параметров подпрограммы. Примеры:
$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, но с неявным сборщиком мусора.
В отличие от локальных переменных в 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.
Если объявлены во внешней области видимости (области видимости файла), то лексические переменные работают примерно как статические переменные файла в C. Они доступны всем функциям в том же файле, объявленные ниже их, но недоступны извне этого файла. Эта стратегия иногда используется в модулях для создания частных переменных, которые может видеть весь модуль.
Временные значения через 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.
Некоторые типы lvalues также могут быть локализованы: элементы и срезы массивов и хешей, условные выражения (при условии, что их результат всегда локализуем) и символические ссылки. Подобно простым переменным, это создаёт новые значения с динамическим областью действия.
Если для local задано более одной переменной или выражения, их нужно поместить в скобки. Этот оператор работает, сохраняя текущие значения этих переменных в списке аргументов в скрытом стеке и восстанавливая их при выходе из блока, подпрограммы или eval. Это означает, что вызываемые подпрограммы также могут ссылаться на локальную переменную, но не на глобальную. Список аргументов можно присвоить, если это необходимо, что позволяет инициализировать локальные переменные. (Если для определённой переменной не указана инициализация, она создаётся с неопределённым значением.)
Поскольку local — оператор, выполняемый во время выполнения, он выполняется каждый раз при проходе цикла. Следовательно, более эффективно локализовать переменные вне цикла.
Примечания к грамматике local()
Оператор local — это просто модификатор выражения lvalue. Когда вы присваиваете значение локализованной переменной, 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 */ не повлияет на внутреннее значение разделителя входных записей.
Локализация элементов составных типов
Стоит также уделить немного времени объяснению того, что происходит при локализации члена составного типа (т. е. элемента массива или хеша). В этом случае элемент локализуется по имени. Это означает, что когда область действия local() заканчивается, сохранённое значение будет восстановлено в элемент хеша, ключ которого был указан в local(), или в элемент массива, индекс которого был указан в local(). Если этот элемент был удалён во время действия local() (например, с помощью delete() из хеша или shift() из массива), он вернётся в существование, возможно, расширяя массив и заполняя пропущенные элементы значением 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() для несуществующих членов составных типов может измениться в будущем. Поведение оператора 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
} за исключением того, что для них local ограничено областью действия 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, ключевое слово state доступно только в Perl 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 vs 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] и т. д. Вы можете изменить все элементы массива, передав все элементы как скаляры, но вам придётся использовать механизм * (или эквивалентный механизм ссылок), чтобы push, pop, или изменять размер массива. Конечно, передача typeglob (или ссылки) будет быстрее.
Даже если вы не хотите изменять массив, этот механизм полезен для передачи нескольких массивов в одном списке, потому что обычно механизм списка объединяет все значения массива, так что вы не сможете извлечь отдельные массивы. Дополнительную информацию о 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()для полного typeglob. Это можно использовать для создания новых записей в таблице символов:sub ioqueue { local (*READER, *WRITER); # not my! pipe (READER, WRITER) or die "pipe: $!"; return (*READER, *WRITER); } ($head, $tail) = ioqueue();См. модуль Symbol для способа создания анонимных записей в таблице символов.
Поскольку присвоение ссылки typeglob создает псевдоним, это можно использовать для создания фактически локальной функции или, по крайней мере, локального псевдонима.
{ 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);
}
} Здесь мы используем typeglob для создания псевдонимов в таблице символов. Хотя это немного тонко, и это не сработает, если вы используете my переменные, потому что только глобальные переменные (даже в виде local) находятся в таблице символов.
Если вы передаете обработку файлов, вы обычно можете использовать просто голый typeglob, как *STDOUT, но ссылки на typeglob тоже работают. Например:
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() будет ссылкой на скаляр, массив, хэш, код или glob.
У необработанных символов прототипов есть специальные значения. Любой необработанный @ или % съедает все оставшиеся аргументы и форсирует контекст списка. Аргумент, представленный $, форсирует контекст скаляра. & требует анонимную подпрограмму, которая, если передана в качестве первого аргумента, не требует ключевого слова sub или последующей запятой.
* разрешает подпрограмме принимать в этом слоте имя, константу, скалярное выражение, typeglob или ссылку на typeglob. Значение будет доступно подпрограмме либо как простой скаляр, либо (в последних двух случаях) как ссылка на typeglob. Если вы хотите всегда преобразовывать такие аргументы в ссылку на typeglob, используйте 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". (Да, все еще есть нерешенные проблемы, связанные с видимостью @_. Я игнорирую этот вопрос на данный момент. (Но обратите внимание, что если мы сделаем @_ лексически ограниченным, эти анонимные подпрограммы могут действовать как замыкания... (Чувствуется, что это немного похоже на Lisp? (Ничего страшного.))))
И вот повторная реализация оператора 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, одна с прототипом () , вызывающим встраивание, а другая без него (вывод депарсера усечен для ясности):
$ 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, оператор 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 {
our $AUTOLOAD; # keep 'use strict' happy
my $program = $AUTOLOAD;
$program =~ s/.*:://;
system($program, @_);
}
date();
who();
ls('-l'); Фактически, если вы предварительно объявляете функции, которые хотите вызвать таким образом, вам даже не нужны скобки:
use subs qw(date who ls);
date;
who;
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.32.0/perlsub