Spec-Zone.ru › Perl 5.34

perlsub

СОДЕРЖАНИЕ

  • ИМЯ
  • СИНТАКСИС
  • ОПИСАНИЕ
    • Подписи
    • Приватные переменные через my()
    • Постоянные приватные переменные
      • Постоянные переменные через state()
      • Постоянные переменные с замыканиями
    • Временные значения через local()
      • Грамматическое примечание к local()
      • Локализация специальных переменных
      • Локализация глобальных переменных
      • Локализация элементов составных типов
      • Локализованное удаление элементов составных типов
    • Подпрограммы lvalue
    • Лексические подпрограммы
      • state sub против my sub
      • Подпрограммы our
    • Передача записей таблицы символов (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

import , unimport , INC

документировано в 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, который работает более похоже на авто-декларации C.

Некоторые типы lvalue также могут быть локализованы: элементы и срезы массивов и хэшей, условные операторы (при условии, что их результат всегда локализуем), и символические ссылки. Как и в случае с простыми переменными, это создаёт новые, динамически области действия значения.

Если оператору 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; в то время избегайте кода, который полагается на определённое поведение локализации привязанных массивов или хэшей (локализацию отдельных элементов всё ещё можно использовать). Подробности см. в "Локализация привязанных массивов и хэшей не работает" в perl58delta.

Локализация дескрипторов

Конструкция

local *name;

создаёт новую запись в таблице символов для дескриптора name в текущем пакете. Это означает, что все переменные в его слоте дескриптора ($name, @name, %name, &name и name дескриптор файла) динамически сбрасываются.

Это подразумевает, среди прочего, что любая магия, в конечном счёте, принесённая этими переменными, локально теряется. Другими словами, сказать local */ не будет иметь никакого влияния на внутреннее значение разделителя входных записей.

Локализация элементов составных типов

Стоит также уделить внимание тому, что происходит, когда вы локализуете член составного типа (т.е. элемент массива или хэша). В этом случае элемент локализуется по имени. Это означает, что когда область действия локализации заканчивается, сохранённое значение будет восстановлено в элемент хэша, ключ которого был указан в локализации, или в элемент массива, индекс которого был указан в локализации. Если этот элемент был удалён во время действия локализации (например, удалением из хэша или среза из массива), он вернётся, возможно, расширяя массив и заполняя пропущенные элементы значением по умолчанию undef. Например, если вы скажете

%hash = ( 'This' => 'is', 'a' => 'test' );
@ary  = ( 0..5 );
{
     local($ary[5]) = 6;
     local($hash{'a'}) = 'drill';
     while (my $e = pop(@ary)) {
         print "$e . . .\n";
         last unless $e > 3;
     }
     if (@ary) {
         $hash{'only a'} = 'test';
         delete $hash{'a'};
     }
}
print join(' ', map { "$_ $hash{$_}" } sort keys %hash),".\n";
print "The array has ",scalar(@ary)," elements: ",
      join(', ', map { defined $_ ? $_ : 'undef' } @ary),"\n";

Perl выведет

6 . . .
4 . . .
3 . . .
This is a test only a test.
The array has 6 elements: 0, 1, 2, undef, undef, 5

Поведение оператора local() для несуществующих членов составных типов может измениться в будущем. Поведение оператора 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 не может. Если вам требуется специальная обработка при хранении и извлечении значений, рассмотрите использование модуля Sentinel CPAN или что-то подобное.

Лексические подпрограммы

Начиная с Perl 5.18, вы можете объявить частную подпрограмму с помощью my или state. Как и в случае со state-переменными, ключевое слово state доступно только в Perl use feature 'state' или выше.

До 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 (или ссылки) будет быстрее.

Даже если вы не хотите изменять массив, этот механизм полезен для передачи нескольких массивов в одном списке, потому что обычно механизм списка объединяет все значения массива, так что вы не сможете извлечь отдельные массивы. Дополнительную информацию о typeglob вы найдете в "Typeglobs and Filehandles" в perldata.

Когда все еще использовать local()

Несмотря на существование my, все еще есть три места, где оператор local по-прежнему хорош. На самом деле, в этих трех местах вы обязаны использовать local вместо my.

  1. Вам нужно присвоить глобальной переменной временное значение, особенно $_.

    Глобальные переменные, такие как @ARGV или переменные с пунктуацией, должны быть local с помощью local(). Этот блок считывает /etc/motd и разбивает его на части, разделенные строками с символами равенства, которые помещаются в @Fields.

    {
        local @ARGV = ("/etc/motd");
        local $/ = undef;
        local $_ = <>;  
        @Fields = split /^\s*=+\s*$/;
    } 

    В частности, важно local $_ в любой процедуре, которая присваивает ей значение. Обратите внимание на неявные присваивания в while условных операторах.

  2. Вам нужно создать локальную обработку файлов или каталогов или локальную функцию.

    Функция, которая нуждается в своей собственной обработке файлов, должна использовать 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 для получения дополнительной информации о манипулировании функциями по имени таким образом.

  3. Вы хотите временно изменить только один элемент массива или хэша.

    Вы можете 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 для простого способа объявления большинства констант.)

Следующие функции будут встраиваться:

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-системе.

Переопределение можно выполнить только импортировав имя из модуля во время компиляции; обычное предварительное объявление недостаточно. Однако, псевдоним 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 выше не реализована вся необходимая поддержка для чистого переопределения оператора Perl's glob. Встроенный 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';

Для получения дополнительной информации о списках атрибутов и их обработке см. attributes и 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.34.0/perlsub

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API