perlsub
СОДЕРЖАНИЕ
- НАЗВАНИЕ
- СИНОПСИС
- ОПИСАНИЕ
- Подписи
- Переменные, скрытые от доступа (my())
- Постоянные переменные, скрытые от доступа
- Временные значения с помощью local()
- Подпрограммы-л-значения
- Лексические подпрограммы
- Передача записей таблицы символов (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 для вызова функций и возвращаемых значений проста: все функции передаются в качестве параметров в виде одного единственного плоского списка скаляров, и все функции аналогичным образом возвращают своему вызывающему объекту один единственный плоский список скаляров. Любые массивы или хеши в этих списках вызовов и возвратов будут сжаты, потеряв свою идентичность, но вы всегда можете использовать передачу по ссылке, чтобы избежать этого. В списки вызовов и возвратов могут входить любое количество скалярных элементов. (Функцию без явного оператора return часто называют подпрограммой, но в Perl нет никакой разницы.)
В подпрограмме, использующей подписи (см. "Подписи" ниже), аргументы присваиваются в лексические переменные, введенные подписью. В текущей реализации Perl они также доступны в массиве @_ аналогично подпрограммам без подписей, но доступ к ним таким способом в настоящее время не рекомендуется внутри подпрограммы, использующей подписи.
В подпрограмме, не использующей подписи, любые передаваемые аргументы появляются в массиве @_. Таким образом, если вы вызвали функцию с двумя аргументами, они будут сохранены в $_[0] и $_[1]. Массив @_ является локальным массивом, но его элементы являются псевдонимами для фактических скалярных параметров. В частности, если элемент $_[0] обновляется, соответствующий аргумент обновляется (или возникает ошибка, если он не обновляемый). Если аргумент является элементом массива или хеша, которого не существовало при вызове функции, этот элемент создаётся только тогда (и если) он изменяется или к нему берется ссылка. (В некоторых предыдущих версиях Perl элемент создавался независимо от того, присваивается ли он.) Присвоение всему массиву @_ удаляет этот псевдоним и не обновляет никакие аргументы.
При отсутствии подписей Perl не предоставляет других способов создания именованных формальных параметров. На практике вы просто присваиваете значения в список my() из этих переменных. Переменные, которые не объявлены как скрытые, являются глобальными переменными. Для подробной информации о создании скрытых переменных см. "Скрытые переменные с помощью my()" и "Временные значения с помощью local()". Чтобы создать защищённую среду для набора функций в отдельном пакете (и, вероятно, в отдельном файле), см. "Пакеты" в perlmod.
Оператор return может использоваться для выхода из подпрограммы, при необходимости указывая возвращаемое значение, которое будет вычислено в соответствующем контексте (список, скаляр или пустота) в зависимости от контекста вызова подпрограммы. Если вы не указываете возвращаемое значение, подпрограмма возвращает пустой список в списочном контексте, неопределенное значение в скалярном контексте или ничего в пустом контексте. Если вы возвращаете одну или несколько агрегаций (массивы и хеши), они будут сжаты в один большой неразличимый список.
Если оператор return не найден, а последним оператором является выражение, его значение возвращается. Если последний оператор — это управляющая структура цикла, например, foreach или while, возвращаемое значение не определено. Пустая подпрограмма возвращает пустой список.
Пример:
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 v5.16. Он будет оцениваться как ссылка на выполняемую в данный момент подпрограмму, что позволяет осуществлять рекурсивные вызовы без предварительного знания имени вашей подпрограммы.
use v5.16;
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 позволяет объявлять формальные параметры подпрограммы специальной синтаксической конструкцией, отдельно от процедурного кода тела подпрограммы. Список формальных параметров называется подписью.
Эта возможность должна быть включена, прежде чем её можно использовать. Она включается автоматически при объявлении с use v5.36 (или выше) или более явно с use feature '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;
} Значение по умолчанию параметра всё ещё будет вычисляться, если соответствующий аргумент не указан, даже если значение нигде не сохраняется. Это в случае, если вычисление имеет важные побочные эффекты. Однако, оно будет вычислено в контексте "пусто", поэтому, если оно не имеет побочных эффектов и не тривиально, оно вызовет предупреждение, если категория предупреждений "пустое" включена. Если значение по умолчанию безымянного необязательного параметра не важно, его можно опустить, как и имя параметра:
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;
} До Perl 5.36 эти подписи считались экспериментальными и выводили предупреждение в категории experimental::signatures. Начиная с Perl 5.36 это больше не происходит, хотя категория предупреждений всё ещё существует для обратной совместимости с кодом, пытающимся её отключить с помощью инструкции, такой как:
no warnings 'experimental::signatures'; В текущей реализации Perl, при использовании подписи, аргументы всё ещё доступны в специальной переменной массива @_. Однако, доступ к ним через этот массив теперь не рекомендуется, и на него не следует полагаться в новом коде, так как эта возможность может измениться в будущей версии. Код, пытающийся получить доступ к массиву @_, будет генерировать предупреждения в категории experimental::args_array_with_signatures при компиляции:
sub f ($x) {
# This line emits the warning seen below
print "Arguments are @_";
} Use of @_ in join or string with signatured subroutine is
experimental at ... Есть разница между двумя способами доступа к аргументам: @_ алиасирует аргументы, но переменные подписи получают копии аргументов. Таким образом, запись в переменную подписи изменяет только эту переменную и не влияет на переменные вызывающего, но запись в элемент @_ изменяет то, что использовал вызывающий для передачи этого аргумента.
Возможна синтаксическая неоднозначность между подписями и прототипами (см. "Прототипы"), поскольку оба начинаются с открывающей скобки и оба могут появляться в некоторых тех же местах, например, сразу после имени в объявлении подпрограммы. По историческим причинам, когда подписи не включены, любая открывающая скобка в таком контексте вызовет очень снисходительную обработку прототипов. Большинство подписей будут интерпретироваться как прототипы в этих случаях, но не будут являться допустимыми прототипами. (Допустимый прототип не может содержать никаких буквенных символов.) Это приведёт к несколько запутанным сообщениям об ошибках.
Чтобы избежать неоднозначности, когда подписи включены, специальный синтаксис для прототипов выключается. Нет попыток угадать, был ли скобочный блок прототипом или подписью. Чтобы дать подпрограмме прототип в этих обстоятельствах, используйте атрибут прототипа атрибута. Например,
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 файле. Если перечисляется более одного значения, список должен быть помещён в скобки. Все перечисленные элементы должны быть допустимыми левыми значениями. Только буквенно-цифровые идентификаторы могут быть лексически ограниченными – магические встроенные функции, такие как $/, в настоящее время должны быть 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 FILE, это не означает, что внутри функции она работает как статическая переменная 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, которая работает больше как объявление автоматической переменной языка 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(), или в элементе массива, индекс которого был указан в 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 Эта конструкция поддерживается с Perl v5.12.
Подпрограммы 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 не может. Если вам требуется специальная обработка при хранении и извлечении значений, рассмотрите использование модуля CPAN Sentinel или что-то подобное.
Лексические подпрограммы
Начиная с Perl 5.18, можно объявлять приватные подпрограммы с my или state. Как и с переменными состояния, ключевое слово state доступно только в Perl use feature 'state' или use v5.10 и выше.
До 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; они поддерживают друг друга (см. "Циклические ссылки" в 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] и т. д. Вы можете изменять все элементы массива, передавая все элементы как скаляры, но вам нужно использовать механизм * (или эквивалентный механизм ссылок), чтобы push, pop, или изменить размер массива. Передача typeglob (или ссылки) будет, безусловно, быстрее.
Даже если вы не хотите изменять массив, этот механизм полезен для передачи нескольких массивов в одном списке, так как обычно механизм списка объединяет все значения массива, поэтому вы не сможете извлечь отдельные массивы. Более подробную информацию о typeglobs см. в "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 для простого способа объявления большинства констант.)
Следующие функции будут встроены:
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]) заключается в том, что произойдёт, если переменная потенциально может быть модифицирована. Например:
BEGIN {
my $x = 10;
*FOO = sub () { $x };
$x++;
}
print FOO(); # printed 10 prior to 5.32.0 Начиная с Perl 5.22 это выдавало предупреждение о устаревании, а начиная с Perl 5.32 — ошибку выполнения. Ранее переменная сразу встраивалась и переставала вести себя как обычная лексическая переменная; поэтому выводилось 10, а не 11.
Если вы всё ещё хотите, чтобы такая подпрограмма встраивалась (без предупреждения), убедитесь, что переменная не используется в контексте, где она может быть модифицирована помимо её объявления.
# Fine, no warning
BEGIN {
my $x = 54321;
*INLINED = sub () { $x };
}
# Error
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 = 10;
*FOO = sub () { return $x };
$x++;
}
print FOO(); # prints 11 Самый простой способ определить, была ли подпрограмма встроена, — использовать 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 {
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'; Дополнительную информацию о списках атрибутов и их обработке см. в атрибутах и Attribute::Handlers.
СМОТРИ ТАКЖЕ
См. "Шаблоны функций" в perlref для получения дополнительной информации о ссылках и замыканиях. См. perlxs, если вы хотите узнать о вызове C-подпрограмм из Perl. См. perlembed, если вы хотите узнать о вызове Perl-подпрограмм из C. См. perlmod, чтобы узнать о группировании ваших функций в отдельных файлах. См. perlmodlib, чтобы узнать, какие модули библиотек поставляются в стандартной комплектации вашей системы. См. perlootut, чтобы узнать, как выполнять вызовы методов объектов.
© 1993–2021 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.36.0/perlsub