Spec-Zone.ru › Perl 5.38

perlref

СОДЕРЖАНИЕ

  • ИМЯ
  • ПРИМЕЧАНИЕ
  • ОПИСАНИЕ
    • Создание ссылок
      • Оператор обратной косой черты
      • Квадратные скобки
      • Фигурные скобки
      • Анонимные подпрограммы
      • Конструкторы
      • Автовивификация
      • Слот типаглоба
    • Использование ссылок
      • Простой скаляр
      • Блок
      • Нотация со стрелкой
      • Объекты
      • Разное использование
    • Циклические ссылки
    • Символические ссылки
    • Не совсем символические ссылки
    • Псевдо-хеши: Использование массива как хеша
    • Шаблоны функций
    • Синтаксис постфиксной дереференции
    • Постфиксное срезание ссылок
    • Присваивание ссылкам
    • Объявление ссылки на переменную
  • ВНИМАНИЕ: Не используйте ссылки в качестве ключей хеша
  • СМОТРИТЕ ТАКЖЕ

ИМЯ

perlref - Perl ссылки и вложенные структуры данных

ПРИМЕЧАНИЕ

Это полное руководство по всем аспектам ссылок. Для более краткого, обучающего введения только в основные функции, см. perlreftut.

ОПИСАНИЕ

До версии 5 Perl было трудно представлять сложные структуры данных, поскольку все ссылки должны были быть символическими — и даже тогда было сложно ссылаться на переменную вместо записи в таблице символов. Теперь Perl не только упрощает использование символических ссылок на переменные, но также позволяет создавать «жёсткие» ссылки на любой фрагмент данных или кода. Любой скаляр может содержать жёсткую ссылку. Поскольку массивы и хеши содержат скаляры, вы теперь можете легко создавать массивы массивов, массивы хешей, хеши массивов, массивы хешей функций и так далее.

Жёсткие ссылки умные — они отслеживают счётчики ссылок за вас, автоматически освобождая объект, на который ссылаются, когда его счётчик ссылок становится равным нулю. (Счётчики ссылок для значений в самоссылочных или циклических структурах данных могут не стать равными нулю без небольшой помощи; см. «Циклические ссылки» для подробного объяснения.) Если это объект, объект уничтожается. См. perlobj для получения дополнительной информации об объектах. (В некотором смысле, всё в Perl является объектом, но мы обычно резервируем это слово для ссылок на объекты, которые официально «благословлены» в пакет класса.)

Символические ссылки — это имена переменных или других объектов, подобно символической ссылке в файловой системе Unix, которая содержит только имя файла. Обозначение *glob является своего рода символической ссылкой. (Символические ссылки иногда называют «мягкими ссылками», но, пожалуйста, не называйте их так; ссылки и так достаточно запутанные без ненужных синонимов.)

В отличие от этого, жёсткие ссылки больше похожи на жёсткие ссылки в файловой системе Unix: они используются для доступа к базовому объекту без учёта его (другого) имени. Когда слово «ссылка» используется без прилагательного, как в следующем абзаце, оно обычно относится к жёсткой ссылке.

Ссылки легко использовать в Perl. Существует только одно основное правило: в общем случае Perl не выполняет неявной привязки или депривязки. Когда скаляр содержит ссылку, он всегда ведёт себя как простой скаляр. Он не превращается магическим образом в массив, хеш или подпрограмму; вы должны явно указать это, выполнив депривязку.

Создание ссылок

Ссылки можно создавать несколькими способами.

Оператор обратной косой черты

Используя оператор обратной косой черты с переменной, подпрограммой или значением. (Это работает так же, как и оператор & (адрес) в C.) Это обычно создаёт ещё одну ссылку на переменную, потому что ссылка на переменную уже существует в таблице символов. Но ссылка на таблицу символов может исчезнуть, и у вас всё равно останется ссылка, возвращённая оператором обратной косой черты. Вот несколько примеров:

$scalarref = \$foo;
$arrayref  = \@ARGV;
$hashref   = \%ENV;
$coderef   = \&handler;
$globref   = \*foo;

Создать настоящую ссылку на дескриптор ввода-вывода (файловый дескриптор или дескриптор каталога) с помощью оператора обратной косой черты нельзя. Максимум, что вы можете получить, это ссылка на типаглоб, которая фактически представляет собой полную запись в таблице символов. Но см. объяснение синтаксиса *foo{THING} ниже. Однако вы всё ещё можете использовать типаглобы и globrefs как дескрипторы ввода-вывода.

Квадратные скобки

Ссылка на анонимный массив может быть создана с помощью квадратных скобок:

$arrayref = [1, 2, ['a', 'b', 'c']];

Здесь мы создали ссылку на анонимный массив из трёх элементов, последним из которых является ссылка на другой анонимный массив из трёх элементов. (Многомерный синтаксис, описанный позже, может быть использован для доступа к этому. Например, после вышеприведённого, $arrayref->[2][1] будет иметь значение «b».)

Получение ссылки на перечисленный список не эквивалентно использованию квадратных скобок — вместо этого это эквивалентно созданию списка ссылок!

@list = (\$a, \@b, \%c);
@list = \($a, @b, %c);      # same thing!

В качестве особого случая, \(@foo) возвращает список ссылок на содержимое @foo, а не ссылку на @foo само по себе. То же самое для %foo, за исключением того, что ссылки на ключи являются копиями (поскольку ключи — это просто строки, а не полноценные скаляры).

Фигурные скобки

Ссылка на анонимный хеш может быть создана с помощью фигурных скобок:

$hashref = {
    'Adam'  => 'Eve',
    'Clyde' => 'Bonnie',
};

Анонимные конструкторы хешей и массивов, подобные этим, можно свободно смешивать, чтобы создавать структуры произвольной сложности. Многомерный синтаксис, описанный ниже, также работает с ними. Вышеприведённые значения — литералы, но переменные и выражения будут работать так же, потому что операторы присваивания в Perl (даже в local() или my()) являются исполняемыми операторами, а не объявлениями времени компиляции.

Поскольку фигурные скобки используются для многих других вещей, включая блоки кода, вам иногда придётся разambiguировать фигурные скобки в начале оператора, поставив + или return, чтобы Perl понимал, что открывающая фигурная скобка не начинает блок кода. Экономия и мнемоническая ценность использования фигурных скобок считаются оправданием этой периодической дополнительной сложности.

Например, если вы хотите создать функцию, которая создаёт новый хеш и возвращает ссылку на него, у вас есть эти варианты:

sub hashem {        { @_ } }   # silently wrong
sub hashem {       +{ @_ } }   # ok
sub hashem { return { @_ } }   # ok

С другой стороны, если вам нужна другая семантика, вы можете сделать так:

sub showem {        { @_ } }   # ambiguous (currently ok,
                               # but may change)
sub showem {       {; @_ } }   # ok
sub showem { { return @_ } }   # ok

Ведущие +{ и {; всегда служат для разambiguирования выражения, чтобы указать, что оно означает либо ссылку на хеш, либо блок кода.

Анонимные подпрограммы

Ссылка на анонимную подпрограмму может быть создана с помощью sub, без имени подпрограммы:

$coderef = sub { print "Boink!\n" };

Обратите внимание на точку с запятой. За исключением того, что код внутри не выполняется сразу, sub {} — это не объявление, а оператор, подобный do{} или eval{}. (Однако, сколько бы раз вы ни выполняли эту строку (если вы не находитесь внутри eval("...")), $coderef всё равно будет ссылаться на ту же самую анонимную подпрограмму.)

Анонимные подпрограммы ведут себя как замыкания относительно переменных my(), то есть переменных, лексически видимых в текущем пространстве имён. Замыкание — понятие из мира Lisp, которое говорит, что если вы определяете анонимную функцию в определённом лексическом контексте, она будет работать в этом контексте даже при вызове вне контекста.

По-человечески, это забавный способ передачи аргументов подпрограмме при её определении, а также при её вызове. Это полезно для создания небольших фрагментов кода, которые нужно выполнить позже, например, обратные вызовы. Вы даже можете использовать их для объектно-ориентированного программирования, хотя Perl уже предоставляет другой механизм для этого — см. perlobj.

Вы также можете рассматривать замыкание как способ написания шаблона подпрограммы без использования eval(). Вот небольшой пример, как работают замыкания:

sub newprint {
    my $x = shift;
    return sub { my $y = shift; print "$x, $y!\n"; };
}
$h = newprint("Howdy");
$g = newprint("Greetings");

# Time passes...

&$h("world");
&$g("earthlings");

Это выведет

Howdy, world!
Greetings, earthlings!

Обратите особое внимание на то, что $x продолжает ссылаться на значение, переданное в newprint(), несмотря на то, что «my $x» выходит из области видимости к тому времени, когда выполняется анонимная подпрограмма. Именно в этом и заключается суть замыкания.

Это относится только к лексическим переменным. Динамические переменные по-прежнему работают так же, как и всегда. Замыкания — это не то, о чём большинство программистов Perl должны беспокоиться в первую очередь.

Конструкторы

Ссылки часто возвращаются специальными подпрограммами, называемыми конструкторами. Объекты Perl — это просто ссылки на специальный тип объектов, которые знают, к какому пакету они относятся. Конструкторы — это просто специальные подпрограммы, которые знают, как создать это соответствие. Они делают это, начиная с обычной ссылки, и ссылка остаётся обычной ссылкой даже при том, что она также является объектом. Конструкторы часто называются new(). Вы можете вызвать их косвенно:

$objref = new Doggie( Tail => 'short', Ears => 'long' );

Но это может привести к неоднозначному синтаксису в некоторых случаях, поэтому часто лучше использовать прямой метод вызова:

$objref   = Doggie->new(Tail => 'short', Ears => 'long');

use Term::Cap;
$terminal = Term::Cap->Tgetent( { OSPEED => 9600 });

use Tk;
$main    = MainWindow->new();
$menubar = $main->Frame(-relief              => "raised",
                        -borderwidth         => 2)

Этот косвенный синтаксис для объектов доступен только при условии, что use feature "indirect" включён, и это не так, когда use v5.36 (или выше) требуется, лучше вообще избегать косвенного синтаксиса для объектов.

Автовивификация

Ссылки соответствующего типа могут возникать, если вы выполняете депривязку с контекстом, предполагающим их существование. Поскольку мы ещё не говорили о депривязке, мы пока не можем показать примеры.

Слот типаглоба

Ссылка может быть создана с помощью специального синтаксиса, известного как синтаксис *foo{THING}. *foo{THING} возвращает ссылку на слот THING в *foo (который является записью в таблице символов, содержащей всё, что известно как foo).

$scalarref = *foo{SCALAR};
$arrayref  = *ARGV{ARRAY};
$hashref   = *ENV{HASH};
$coderef   = *handler{CODE};
$ioref     = *STDIN{IO};
$globref   = *foo{GLOB};
$formatref = *foo{FORMAT};
$globname  = *foo{NAME};    # "foo"
$pkgname   = *foo{PACKAGE}; # "main"

Большинство из них самодокументированы, но *foo{IO} заслуживает особого внимания. Он возвращает дескриптор ввода-вывода, используемый для файловых дескрипторов ("open" в perlfunc), сокетов ("socket" в perlfunc и "socketpair" в perlfunc), и дескрипторов каталогов ("opendir" в perlfunc). Для совместимости с предыдущими версиями Perl, *foo{FILEHANDLE} является синонимом для *foo{IO}, хотя это не рекомендуется, чтобы поощрить использование одного имени: IO. В Perl между v5.8 и v5.22 будет выведено предупреждение о устаревании, но это предупреждение с тех пор отменено.

*foo{THING} возвращает undef, если конкретная ВЕЩЬ ещё не использовалась, за исключением скаляров. *foo{SCALAR} возвращает ссылку на анонимный скаляр, если $foo ещё не использовался. Это может измениться в будущих версиях.

*foo{NAME} и *foo{PACKAGE} являются исключением, так как они возвращают строки, а не ссылки. Они возвращают пакет и имя самого typeglob, а не того, который был ему назначен. Итак, после *foo=*Foo::bar, *foo станет "*Foo::bar", когда используется как строка, но *foo{PACKAGE} и *foo{NAME} будут по-прежнему возвращать "main" и "foo" соответственно.

*foo{IO} является альтернативой механизму *HANDLE, описанному в "Typeglobs и файловые дескрипторы" в perldata для передачи файловых дескрипторов в подпрограммы или хранения в более крупных структурах данных. Его недостатком является то, что он не создаст для вас новый файловый дескриптор. Его преимущество заключается в том, что у вас меньше риска повредить больше, чем вам нужно, присваивая typeglob. (Он по-прежнему объединяет файловые и каталожные дескрипторы.) Однако, если вы присваиваете входящее значение скаляру, а не typeglob, как мы делаем в примерах ниже, такого риска нет.

splutter(*STDOUT);          # pass the whole glob
splutter(*STDOUT{IO});      # pass both file and dir handles

sub splutter {
    my $fh = shift;
    print $fh "her um well a hmmm\n";
}

$rec = get_rec(*STDIN);     # pass the whole glob
$rec = get_rec(*STDIN{IO}); # pass both file and dir handles

sub get_rec {
    my $fh = shift;
    return scalar <$fh>;
}

Использование ссылок

Вот всё о создании ссылок. К этому моменту вы, вероятно, умираете от желания узнать, как использовать ссылки, чтобы вернуться к вашим потерянным данным. Существует несколько основных методов.

Простой скаляр

В любом месте, где вы бы поместили идентификатор (или цепочку идентификаторов) как часть имени переменной или подпрограммы, вы можете заменить идентификатор простым скалярной переменной, содержащей ссылку соответствующего типа:

$bar = $$scalarref;
push(@$arrayref, $filename);
$$arrayref[0] = "January";
$$hashref{"KEY"} = "VALUE";
&$coderef(1,2,3);
print $globref "output\n";

Важно понимать, что мы не дереференцируем $arrayref[0] или $hashref{"KEY"} там. Дело в том, что дереференциация скалярной переменной происходит до того, как произойдёт поиск по ключам. Любое более сложное, чем простая скалярная переменная, должно использовать методы 2 или 3 ниже. Однако «простой скаляр» включает идентификатор, который сам рекурсивно использует метод 1. Следовательно, следующее выводит «привет».

$refrefref = \\\"howdy";
print $$$$refrefref;

Блок

В любом месте, где вы бы поместили идентификатор (или цепочку идентификаторов) как часть имени переменной или подпрограммы, вы можете заменить идентификатор блоком, возвращающим ссылку соответствующего типа. Другими словами, предыдущие примеры можно записать так:

$bar = ${$scalarref};
push(@{$arrayref}, $filename);
${$arrayref}[0] = "January";
${$hashref}{"KEY"} = "VALUE";
&{$coderef}(1,2,3);
$globref->print("output\n");  # iff IO::Handle is loaded

Надо признать, что в этом случае использовать фигурные скобки немного глупо, но блок может содержать любое произвольное выражение, в частности, индексированные выражения:

&{ $dispatch{$index} }(1,2,3);      # call correct routine

Из-за возможности опускать фигурные скобки для простого случая $$x, люди часто ошибаются, рассматривая символы дереференциации как надлежащие операторы и задаются вопросом о их приоритете. Если бы они были такими, вы могли бы использовать скобки вместо фигурных скобок. Это не так. Обратите внимание на разницу ниже; случай 0 — это сокращённая версия случая 1, а не случая 2:

$$hashref{"KEY"}   = "VALUE";       # CASE 0
${$hashref}{"KEY"} = "VALUE";       # CASE 1
${$hashref{"KEY"}} = "VALUE";       # CASE 2
${$hashref->{"KEY"}} = "VALUE";     # CASE 3

Случай 2 также вводит в заблуждение тем, что вы обращаетесь к переменной под названием %hashref, а не де-референцируете через $hashref к хэшу, к которому она, предположительно, ссылается. Это был бы случай 3.

Нотация со стрелкой

Вызовы подпрограмм и поиск отдельных элементов массива встречаются достаточно часто, чтобы использование метода 2 становилось громоздким. В качестве синтаксического сахара примеры для метода 2 могут быть записаны:

$arrayref->[0] = "January";   # Array element
$hashref->{"KEY"} = "VALUE";  # Hash element
$coderef->(1,2,3);            # Subroutine call

Левая часть стрелки может быть любым выражением, возвращающим ссылку, включая предыдущую дереференциацию. Обратите внимание, что $array[$x] не то же самое, что и $array->[$x] здесь:

$array[$x]->{"foo"}->[0] = "January";

Это один из случаев, о которых мы упоминали ранее, когда ссылки могли возникать в контексте lvalue. Перед этим утверждением $array[$x] могла быть неопределённой. Если это так, она автоматически определяется как ссылка на хеш, чтобы мы могли обратиться к {"foo"} в нём. Аналогично $array[$x]->{"foo"} автоматически определится как ссылка на массив, чтобы мы могли обратиться к [0] в нём. Этот процесс называется автовивификацией.

Ещё кое-что здесь. Стрелка необязательна между индексами в квадратных скобках, поэтому вы можете сократить вышесказанное до

$array[$x]{"foo"}[0] = "January";

Что в вырожденном случае использования только обычных массивов даёт вам многомерные массивы, как в C:

$score[$x][$y][$z] += 42;

Ну, ладно, не совсем как массивы C. C не знает, как динамически увеличивать свои массивы. Perl умеет.

Объекты

Если ссылка является ссылкой на объект, то, вероятно, есть методы доступа к этим вещам, и вы, вероятно, должны придерживаться этих методов, если вы не находитесь в пакете класса, который определяет методы объекта. Другими словами, будьте вежливы и не нарушайте инкапсуляцию объекта без веской причины. Perl не навязывает инкапсуляцию. Мы здесь не тоталитарии. Однако мы ожидаем некоторой базовой вежливости.

Разное использование

Использование строки или числа в качестве ссылки создаёт символическую ссылку, как описано выше. Использование ссылки в качестве числа даёт целое число, представляющее её местоположение в памяти. Единственное полезное действие, которое можно с этим сделать, — это сравнить две ссылки численно, чтобы узнать, ссылаются ли они на одно и то же место.

if ($ref1 == $ref2) {  # cheap numeric compare of references
    print "refs 1 and 2 refer to the same thing\n";
}

Использование ссылки как строки даёт тип того, на что она ссылается, включая любое благословление пакета, как описано в perlobj, а также числовой адрес, выраженный в шестнадцатеричном формате. Оператор ref() возвращает только тип того, на что указывает ссылка, без адреса. См. "ref" в perlfunc для получения подробностей и примеров использования.

Оператор bless() можно использовать для связывания объекта, на который указывает ссылка, с пакетом, функционирующим как класс объектов. См. perlobj.

Typeglob можно де-референцировать так же, как и ссылку, потому что синтаксис дереференциации всегда указывает желаемый тип ссылки. Итак, ${*foo} и ${\$foo} оба указывают на одну и ту же скалярную переменную.

Вот трюк для интерполяции вызова подпрограммы в строку:

print "My sub returned @{[mysub(1,2,3)]} that time.\n";

Способ его работы заключается в том, что когда @{...} виден в строке с двойными кавычками, он вычисляется как блок. Блок создаёт ссылку на анонимный массив, содержащий результаты вызова mysub(1,2,3). Таким образом, весь блок возвращает ссылку на массив, который затем де-референцируется @{...} и помещается в строку с двойными кавычками. Этот трюк также полезен для произвольных выражений:

print "That yields @{[$n + 5]} widgets\n";

Аналогично, выражение, возвращающее ссылку на скаляр, может быть де-референцировано посредством ${...}. Таким образом, вышеприведённое выражение можно переписать как:

print "That yields ${\($n + 5)} widgets\n";

Циклические ссылки

В Perl можно создать «циклическую ссылку», которая может привести к утечке памяти. Циклическая ссылка возникает, когда две ссылки содержат ссылку друг на друга, как в этом примере:

my $foo = {};
my $bar = { foo => $foo };
$foo->{bar} = $bar;

Вы также можете создать циклическую ссылку с помощью одной переменной:

my $foo;
$foo = \$foo;

В этом случае счётчик ссылок для переменных никогда не достигнет 0, и ссылки никогда не будут собраны сборщиком мусора. Это может привести к утечке памяти.

Поскольку объекты в Perl реализованы как ссылки, можно также создать циклические ссылки с объектами. Представьте класс TreeNode, где каждый узел ссылается на своих родительские и дочерние узлы. Любой узел с родителем будет участвовать в циклической ссылке.

Вы можете прервать циклические ссылки, создав «слабую ссылку». Слабая ссылка не увеличивает счётчик ссылок для переменной, что означает, что объект может выйти из области видимости и быть уничтожен. Вы можете ослабить ссылку с помощью функции weaken модуля Scalar::Util или как builtin::weaken напрямую в Perl версии 5.35.7 или новее.

Вот как мы можем сделать первый пример безопаснее:

use Scalar::Util 'weaken';

my $foo = {};
my $bar = { foo => $foo };
$foo->{bar} = $bar;

weaken $foo->{bar};

Ссылка от $foo к $bar была ослаблена. Когда переменная $bar выходит из области видимости, она будет собрана сборщиком мусора. В следующий раз, когда вы посмотрите на значение ключа $foo->{bar}, оно будет undef.

Это действие на расстоянии может быть запутанным, поэтому следует быть осторожными при использовании weaken. Вы должны ослабить ссылку в переменной, которая выйдет из области видимости первой. Таким образом, более долгоживущая переменная будет содержать ожидаемую ссылку до тех пор, пока она не выйдет из области видимости.

Символические ссылки

Мы сказали, что ссылки возникают по мере необходимости, если они не определены, но не сказали, что происходит, если значение, используемое в качестве ссылки, уже определено, но не является жёсткой ссылкой. Если вы используете его как ссылку, он будет рассматриваться как символическая ссылка. То есть значение скаляра будет рассматриваться как имя переменной, а не как прямая ссылка на (возможно) анонимное значение.

Люди часто ожидают, что это будет работать так. И так оно и есть.

$name = "foo";
$$name = 1;                 # Sets $foo
${$name} = 2;               # Sets $foo
${$name x 2} = 3;           # Sets $foofoo
$name->[0] = 4;             # Sets $foo[0]
@$name = ();                # Clears @foo
&$name();                   # Calls &foo()
$pack = "THAT";
${"${pack}::$name"} = 5;    # Sets $THAT::foo without eval

Это мощно и немного опасно, так как возможно (с наилучшими намерениями) намереваться использовать жёсткую ссылку и случайно использовать символическую ссылку вместо неё. Чтобы защититься от этого, вы можете сказать

use strict 'refs';

а затем только жёсткие ссылки будут разрешены в течение остальной части вложенного блока. Вложенный блок может отменить это с

no strict 'refs';

Только переменные пакета (глобальные, даже если локализованные) видны для символических ссылок. Лексические переменные (объявленные с помощью my()) не находятся в таблице символов и, следовательно, невидимы для этого механизма. Например:

local $value = 10;
$ref = "value";
{
    my $value = 20;
    print $$ref;
}

Это всё равно будет выводить 10, а не 20. Помните, что local() влияет на переменные пакета, которые все являются «глобальными» для пакета.

Не совсем символические ссылки

Скобки вокруг символической ссылки могут просто служить для изоляции идентификатора или имени переменной от остальной части выражения, как это всегда было внутри строки. Например,

$push = "pop on ";
print "${push}over";

всегда означало вывести "перепрыгни", даже если push — это зарезервированное слово. Это обобщается, чтобы работать так же без окружающих двойных кавычек, так что

print ${push} . "over";

и даже

print ${ push } . "over";

будут иметь тот же эффект. Эта конструкция не считается символической ссылкой, когда вы используете strict refs:

use strict 'refs';
${ bareword };      # Okay, means $bareword.
${ "bareword" };    # Error, symbolic reference.

Аналогично, из-за всего индексирования, выполняемого с использованием отдельных слов, то же самое правило относится к любому bareword, который используется для индексирования хеша. Итак, вместо того, чтобы писать

$hash{ "aaa" }{ "bbb" }{ "ccc" }

вы можете написать просто

$hash{ aaa }{ bbb }{ ccc }

и не беспокоиться о том, являются ли подстрочные индексы зарезервированными словами. В редком случае, если вам нужно сделать что-то вроде

$hash{ shift }

вы можете принудительно интерпретировать это как зарезервированное слово, добавив что-либо, что делает его больше, чем простое слово:

$hash{ shift() }
$hash{ +shift }
$hash{ shift @_ }

Директива use warnings или переключатель -w предупредит вас, если она интерпретирует зарезервированное слово как строку. Но она больше не будет предупреждать вас об использовании строчных слов, потому что строка фактически заключена в кавычки.

Псевдо-хеши: Использование массива как хеша

Псевдо-хеши были удалены из Perl. Директива 'fields' остаётся доступной.

Шаблоны функций

Как объяснялось выше, анонимная функция с доступом к лексическим переменным, видимым при компиляции этой функции, создаёт замыкание. Она сохраняет доступ к этим переменным, даже если она не выполняется до тех пор, пока не будет запущена, например, в обработчике сигналов или обратном вызове Tk.

Использование замыкания в качестве шаблона функции позволяет нам генерировать много функций, которые действуют аналогично. Предположим, вам нужны функции, названные в честь цветов, которые генерируют изменения шрифта HTML для различных цветов:

print "Be ", red("careful"), "with that ", green("light");

Функции red() и green() будут похожи. Чтобы создать их, мы присвоим замыкание typeglob имени функции, которую мы пытаемся создать.

@colors = qw(red blue green yellow orange purple violet);
for my $name (@colors) {
    no strict 'refs';       # allow symbol table manipulation
    *$name = *{uc $name} = sub { "<FONT COLOR='$name'>@_</FONT>" };
}

Теперь все эти различные функции, похоже, существуют независимо. Вы можете вызвать red(), RED(), blue(), BLUE(), green() и т.д. Этот метод экономит время компиляции и использование памяти, а также менее подвержен ошибкам, так как проверки синтаксиса происходят во время компиляции. Важно, чтобы все переменные в анонимном подпрограмме были лексическими, чтобы создать надлежащее замыкание. Это причина для my в переменной итерации цикла.

Это одно из немногих мест, где придание прототипа замыканию имеет смысл. Если вы хотите наложить скалярный контекст на аргументы этих функций (вероятно, не самая разумная идея для этого конкретного примера), вы могли бы написать это так:

*$name = sub ($) { "<FONT COLOR='$name'>$_[0]</FONT>" };

Однако, поскольку проверка прототипов происходит во время компиляции, присвоение выше происходит слишком поздно, чтобы быть полезным. Вы можете решить это, поместив весь цикл присваиваний в блок BEGIN, заставляя его произойти во время компиляции.

Доступ к лексическим переменным, которые меняются со временем — таким, как те, что в for цикле выше, по сути, псевдонимы элементов из окружающих лексических областей видимости — работает только с анонимными подпрограммами, а не с именованными подпрограммами. В целом, именованные подпрограммы не вложены должным образом и должны быть объявлены только в основном пространстве имён.

Это потому, что именованные подпрограммы создаются во время компиляции, поэтому их лексические переменные присваиваются родительским лексическим переменным с первого выполнения родительского блока. Если родительская область видимости вводится во второй раз, её лексические переменные создаются снова, в то время как вложенные подпрограммы по-прежнему ссылаются на старые.

Анонимные подпрограммы могут захватывать каждый раз, когда вы выполняете оператор sub, так как они создаются на лету. Если вы привыкли использовать вложенные подпрограммы в других языках программирования со своими собственными закрытыми переменными, вам придётся немного поработать над этим в Perl. Интуитивное кодирование такого типа ведёт к таинственным предупреждениям о том, что "не останется общим", из-за причин, объяснённых выше. Например, это не сработает:

sub outer {
    my $x = $_[0] + 35;
    sub inner { return $x * 19 }   # WRONG
    return $x + inner();
}

Обходной путь следующий:

sub outer {
    my $x = $_[0] + 35;
    local *inner = sub { return $x * 19 };
    return $x + inner();
}

Теперь inner() может вызываться только изнутри outer(), из-за временных присвоений анонимной подпрограммы. Но когда это происходит, она имеет нормальный доступ к лексической переменной $x из области видимости outer() в момент вызова outer.

Это имеет интересный эффект создания функции, локальной для другой функции, чего Perl обычно не поддерживает.

Синтаксис постфиксной деференции

Начиная с версии 5.20.0, доступен постфиксный синтаксис для использования ссылок. Он ведёт себя так, как описано в "Использование ссылок", но вместо префиксного знака используется постфиксный знак-звёздочка.

Например:

$r = \@a;
@b = $r->@*; # equivalent to @$r or @{ $r }

$r = [ 1, [ 2, 3 ], 4 ];
$r->[1]->@*;  # equivalent to @{ $r->[1] }

В Perl 5.20 и 5.22 этот синтаксис должен быть включён с помощью use feature 'postderef'. Начиная с Perl 5.24, для его использования не требуется объявление функций.

Постфиксная деференция должна работать во всех случаях, когда работала блочная (циркумфиксная) деференция, и должна быть полностью эквивалентной. Определены следующие эквивалентности:

$sref->$*;  # same as  ${ $sref }
$aref->@*;  # same as  @{ $aref }
$aref->$#*; # same as $#{ $aref }
$href->%*;  # same as  %{ $href }
$cref->&*;  # same as  &{ $cref }
$gref->**;  # same as  *{ $gref }

Обратите особое внимание, что $cref->&* не эквивалентно $cref->(), и может служить для различных целей.

Элементы глобального пространства имён могут быть извлечены с помощью постфиксной деференции:

$gref->*{SCALAR}; # same as *{ $gref }{SCALAR}

Постфиксная деференция массивов и скаляров может использоваться в интерполируемых строках (в двойных кавычках или операторе qq), но только если включена функция postderef_qq. Поддержка интерполяции постфиксного доступа к индексу высшего порядка массива (->$#*) также включена, когда функция postderef_qq включена.

Постфиксное срезание ссылок

Срезы значений массивов и хешей также могут быть взяты с помощью постфиксной нотации деференции, с соблюдением следующих эквивалентностей:

$aref->@[ ... ];  # same as @$aref[ ... ]
$href->@{ ... };  # same as @$href{ ... }

Постфиксное срезание пар ключ/значение, добавленное в 5.20.0 и описанное в разделе «Ключ/Значение Хеш Срезы» в perldata, также работает как ожидается:

$aref->%[ ... ];  # same as %$aref[ ... ]
$href->%{ ... };  # same as %$href{ ... }

Как и с постфиксным массивом, постфиксная деференция среза значений может использоваться в интерполируемых строках (в двойных кавычках или операторе qq), но только если включена функция postderef_qq feature.

Присваивание ссылкам

Начиная с версии 5.22.0, к оператору ссылки можно присвоить значение. Он выполняет операцию алиасирования, так что имя переменной, к которой ссылаются слева, становится псевдонимом для объекта, к которому ссылаются справа:

\$a = \$b; # $a and $b now point to the same scalar
\&foo = \&bar; # foo() now means bar()

Этот синтаксис должен быть включён с помощью use feature 'refaliasing'. Он экспериментальный и по умолчанию выдаёт предупреждение, если no warnings 'experimental::refaliasing' не включена.

Эти формы могут быть присвоены, и вызовут правое выражение в скалярном контексте:

\$scalar
\@array
\%hash
\&sub
\my $scalar
\my @array
\my %hash
\state $scalar # or @array, etc.
\our $scalar   # etc.
\local $scalar # etc.
\local our $scalar # etc.
\$some_array[$index]
\$some_hash{$key}
\local $some_array[$index]
\local $some_hash{$key}
condition ? \$this : \$that[0] # etc.

Операции среза и скобки вызывают правое выражение в списке контекста:

\@array[5..7]
(\@array[5..7])
\(@array[5..7])
\@hash{'foo','bar'}
(\@hash{'foo','bar'})
\(@hash{'foo','bar'})
(\$scalar)
\($scalar)
\(my $scalar)
\my($scalar)
(\@array)
(\%hash)
(\&sub)
\(&sub)
\($foo, @bar, %baz)
(\$foo, \@bar, \%baz)

Каждый элемент справа должен быть ссылкой на объект соответствующего типа. Скобки сразу после массива (и возможно также my/state/our/local) превратят каждый элемент массива в псевдоним соответствующей скалярной ссылке справа:

\(@a) = \(@b); # @a and @b now have the same elements
\my(@a) = \(@b); # likewise
\(my @a) = \(@b); # likewise
push @a, 3; # but now @a has an extra element that @b lacks
\(@a) = (\$a, \$b, \$c); # @a now contains $a, $b, and $c

Объединение этой формы с local и помещение скобок сразу после хеша запрещены (потому что неясно, что они должны делать):

\local(@array) = foo(); # WRONG
\(%hash)       = bar(); # WRONG

Присвоение ссылкам и не-ссылкам может быть объединено в списки и условных выражениях "тернарное условие", при условии, что значения справа являются правильного типа для каждого элемента слева, хотя это может привести к запутанному коду:

(my $tom, \my $dick, \my @harry) = (\1, \2, [1..3]);
# $tom is now \1
# $dick is now 2 (read-only)
# @harry is (1,2,3)

my $type = ref $thingy;
($type ? $type eq 'ARRAY' ? \@foo : \$bar : $baz) = $thingy;

Цикл foreach также может использовать конструктор ссылок для переменной цикла, хотя синтаксис ограничен одним из следующих, с необязательным my, state, или our после обратной косой черты:

\$s
\@a
\%h
\&c

Скобки недопустимы. Эта функция особенно полезна для массивов массивов или массивов хешей:

foreach \my @a (@array_of_arrays) {
    frobnicate($a[0], $a[-1]);
}

foreach \my %h (@array_of_hashes) {
    $h{gelastic}++ if $h{type} eq 'funny';
}

ПРЕДУПРЕЖДЕНИЕ: Алиасирование не работает корректно с замыканиями. Если вы попытаетесь сделать алиас лексических переменных из вложенной подпрограммы или eval, алиасирование будет видно только внутри этой вложенной подпрограммы и не повлияет на внешнюю подпрограмму, где переменные объявлены. Это странное поведение может измениться.

Объявление ссылки на переменную

Начиная с версии 5.26.0, оператор ссылки может стоять после my, state, our, или local. Этот синтаксис должен быть включён с помощью use feature 'declared_refs'. Он экспериментальный и по умолчанию выдаёт предупреждение, если no warnings 'experimental::refaliasing' не включена.

Эта функция делает следующее:

my \$x;
our \$y;

эквивалентным:

\my $x;
\our $x;

Она предназначена в основном для использования в присвоениях ссылкам (см. "Присваивание ссылкам", выше). Она также позволяет использовать обратную косую черту только для некоторых элементов в списке объявленных переменных:

my ($foo, \@bar, \%baz); # equivalent to:  my $foo, \my(@bar, %baz);

ПРЕДУПРЕЖДЕНИЕ: Не используйте ссылки в качестве ключей хеша

Вы не можете (полезно) использовать ссылку в качестве ключа хеша. Она будет преобразована в строку:

$x{ \$a } = $a;

Если вы попытаетесь сделать деференцию ключа, она не выполнит жёсткую деференцию, и вы не достигнете желаемого результата. Вам, возможно, захочется сделать что-то вроде

$r = \@a;
$x{ $r } = $r;

И тогда вы можете использовать values(), которые будут реальными ссылками, вместо keys(), которые ими не будут.

Модуль Tie::RefHash предоставляет удобный обходной путь к этому.

См. также

Помимо очевидных документов, исходный код может быть информативным. Некоторые патологические примеры использования ссылок можно найти в тесте обратной совместимости t/op/ref.t в каталоге исходного кода Perl.

См. также perldsc и perllol, чтобы узнать, как использовать ссылки для создания сложных структур данных, и perlootut и perlobj, чтобы узнать, как их использовать для создания объектов.

© 1993–2023 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.38.0/perlref

Spec-Zone.ru

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