perlref
СОДЕРЖАНИЕ
- ИМЯ
- ПРИМЕЧАНИЕ
- ОПИСАНИЕ
- ВНИМАНИЕ: Не используйте ссылки в качестве ключей хэша
- Объявление ссылки на переменную
- СМОТРИТЕ ТАКЖЕ
ИМЯ
perlref - Ссылки Perl и вложенные структуры данных
ПРИМЕЧАНИЕ
Это полная документация по всем аспектам ссылок. Для более краткого вводного урока по основным функциям см. perlreftut.
ОПИСАНИЕ
До версии 5 Perl было сложно представлять сложные структуры данных, поскольку все ссылки должны были быть символическими — и даже тогда было трудно ссылаться на переменную вместо записи в таблице символов. Теперь Perl не только упрощает использование символических ссылок на переменные, но также позволяет иметь «жёсткие» ссылки на любой фрагмент данных или код. Любой скаляр может содержать жёсткую ссылку. Поскольку массивы и хэши содержат скаляры, вы теперь можете легко создавать массивы массивов, массивы хэшей, хэши массивов, массивы хэшей функций и так далее.
Жёсткие ссылки разумные — они отслеживают счётчики ссылок за вас, автоматически освобождая то, на что ссылаются, когда их счётчик ссылок становится равным нулю. (Счётчики ссылок для значений в самоссылочных или циклических структурах данных могут не достигнуть нуля без некоторой помощи; см. «Циклические ссылки» для подробного объяснения.) Если это окажется объектом, объект будет разрушен. См. perlobj для получения дополнительной информации об объектах. (В некотором смысле, всё в Perl является объектом, но мы обычно используем это слово для ссылок на объекты, которые официально «благословлены» в пакет класса.)
Символические ссылки — это имена переменных или других объектов, так же, как символическая ссылка в файловой системе Unix содержит только имя файла. Обозначение *glob — это своего рода символическая ссылка. (Символические ссылки иногда называют «мягкими ссылками», но, пожалуйста, не называйте их так; ссылки достаточно запутанные без бесполезных синонимов.)
В отличие от этого, жёсткие ссылки больше похожи на жёсткие ссылки в файловой системе Unix: они используются для доступа к базовому объекту, не беспокоясь о том, каково его (другое) имя. Когда слово «ссылка» используется без прилагательного, как в следующем абзаце, оно обычно относится к жёсткой ссылке.
Ссылки легко использовать в Perl. Существует лишь одно основное правило: в общем случае Perl не выполняет неявной ссылки или де-референции. Когда скаляр хранит ссылку, он всегда ведет себя как простой скаляр. Он не начинает волшебным образом становиться массивом, хэшем или подпрограммой; вы должны явно указать ему это, выполняя де-референцию.
Создание ссылок
Ссылки можно создавать несколькими способами.
- 1.
-
Используя оператор обратной косой черты (backslash) для переменной, подпрограммы или значения. (Это работает очень похоже на оператор & (address-of) в 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()) являются исполняемыми операторами, а не объявлениями на этапе компиляции.
Поскольку фигурные скобки используются для нескольких других вещей, включая блоки (BLOCKs), вам иногда может потребоваться разобрать скобки в начале оператора, поставив
+или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 по-прежнему будет ссылаться на ту же самую анонимную подпрограмму.)Анонимные подпрограммы действуют как замыкания (closures) относительно переменных 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 будет выведено предупреждение о устаревании, но это предупреждение с тех пор было отменено.*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 и файловые дескрипторы" в 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 рекурсивно. Поэтому приведенный ниже код выведет «howdy».$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.
Вот как мы можем сделать первый пример безопаснее:
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"; всегда означало вывести «pop on 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.
ПРЕДУПРЕЖДЕНИЕ: Не использовать ссылки в качестве ключей хеша
Вы не можете (полезно) использовать ссылку в качестве ключа хеша. Она будет преобразована в строку:
$x{ \$a } = $a; Если вы попытаетесь выполнить обращение к ключу, это не будет жёстким обращением, и вы не добьётесь того, что пытаетесь сделать. Возможно, вы хотели бы сделать что-то вроде
$r = \@a;
$x{ $r } = $r; И тогда вы, по крайней мере, можете использовать значения(), которые будут реальными ссылками, вместо ключей(), которые не будут.
Модуль 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 Присваивание ссылкам и не ссылкам может быть объединено в списках и условных выражениях тернарного оператора, если значения в правой части соответствуют типу каждого элемента слева, хотя это может привести к запутанному коду:
(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.30.3/perlref