Spec-Zone.ru › Perl 5.36

perlref

СОДЕРЖАНИЕ

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

ИМЯ

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

ЗАМЕЧАНИЕ

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

ОПИСАНИЕ

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

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

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

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

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

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

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

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

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

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

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

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

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

$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()) являются выполняемыми операторами, а не объявлениями на этапе компиляции.

Поскольку фигурные скобки используются для нескольких других целей, включая блоки, вам иногда придётся избегать неоднозначности фигурных скобок в начале оператора, поместив + или 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

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

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

Ссылка на анонимную подпрограмму может быть создана с помощью 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 (или выше) запрошен, лучше избегать косвенного синтаксиса для объектов.

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

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

Слот typeglob

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

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

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.

Типглоба можно разыменовать так же, как и ссылку, потому что синтаксис разыменования всегда указывает на желаемый тип ссылки. Итак, ${*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.

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

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

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

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

$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.

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

$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() будут похожи. Для их создания мы назначим замыкание типуглобу с именем функции, которую мы пытаемся создать.

@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 включена.

Постфиксное сечение ссылки

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

$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–2021 Larry Wall and others
Licensed under the GNU General Public License version 1 or later, or the Artistic License.
The Perl logo is a trademark of the Perl Foundation.
https://perldoc.perl.org/5.36.0/perlref

Spec-Zone.ru

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