Spec-Zone.ru › Perl 5.28

perlref

СОДЕРЖАНИЕ

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

НАЗВАНИЕ

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

ПРИМЕЧАНИЕ

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

ОПИСАНИЕ

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

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

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

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

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

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

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

1.

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

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

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

2.

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

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

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

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

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

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

3.

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

$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

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

4.

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

5.

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

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

7.

Ссылка может быть создана с помощью специального синтаксиса, известного как синтаксис *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 будет выдаваться предупреждение о depreкации, но эта depreкация с тех пор была отменена.

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

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

*foo{IO} — это альтернатива механизму *HANDLE, описанному в "Typeglobs and Filehandles" в 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>;
}

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

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

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

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

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

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

  4. Если ссылка оказывается ссылкой на объект, то, вероятно, существуют методы для доступа к ссылаемым объектам, и вы, скорее всего, должны придерживаться этих методов, если вы не находитесь в пакете класса, который определяет методы объекта. Другими словами, будьте вежливы и не нарушайте инкапсуляцию объекта без очень веской причины. 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.

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

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.

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

$array{ "aaa" }{ "bbb" }{ "ccc" }

можно написать просто

$array{ aaa }{ bbb }{ ccc }

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

$array{ shift }

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

$array{ shift() }
$array{ +shift }
$array{ 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.

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

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

$x{ \$a } = $a;

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

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

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

Стандартный модуль Tie::RefHash предоставляет удобное решение этой проблемы.

Синтаксис постфиксного разыменования

Начиная с версии 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

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

(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);

См. также

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

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

© 1993–2020 Larry Wall and others
Licensed under the GNU General Public License version 1 or later, or the Artistic License.
The Perl logo is a trademark of the Perl Foundation.
https://perldoc.perl.org/5.28.3/perlref

Spec-Zone.ru

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