perlref
СОДЕРЖАНИЕ
- ИМЯ
- ЗАМЕЧАНИЕ
- ОПИСАНИЕ
- ПРЕДУПРЕЖДЕНИЕ: Не используйте ссылки в качестве ключей хеша
- Объявление ссылки на переменную
- СМОТРИТЕ ТАКЖЕ
ИМЯ
perlref - Ссылки Perl и вложенные структуры данных
ЗАМЕЧАНИЕ
Это полная документация по всем аспектам ссылок. Для более краткого, вводного учебника по основным функциям см. perlreftut.
ОПИСАНИЕ
До версии 5 Perl было сложно представлять сложные структуры данных, так как все ссылки должны были быть символическими — и даже тогда было сложно сослаться на переменную вместо записи в таблице символов. Теперь Perl не только упрощает использование символических ссылок на переменные, но также позволяет иметь «жёсткие» ссылки на любой фрагмент данных или кода. Любой скаляр может содержать жёсткую ссылку. Поскольку массивы и хеши содержат скаляры, вы можете легко создать массивы массивов, массивы хешей, хеши массивов, массивы хешей функций и так далее.
Жёсткие ссылки умные — они отслеживают счётчики ссылок за вас, автоматически освобождая ссылку на объект, когда его счётчик ссылок становится равным нулю. (Счётчики ссылок для значений в самоссылочных или циклических структурах данных могут не стать равными нулю без небольшой помощи; см. «Циклические ссылки» для подробного объяснения.) Если это объект, объект уничтожается. См. perlobj для получения дополнительной информации об объектах. (В некотором смысле, всё в Perl — объект, но мы обычно используем это слово для ссылок на объекты, которые были официально «благословлены» в пакет класса.)
Символические ссылки — это имена переменных или других объектов, точно так же, как символическая ссылка в файловой системе Unix содержит только имя файла. Нотация *glob — это своего рода символическая ссылка. (Символические ссылки иногда называют «мягкими ссылками», но, пожалуйста, не называйте их так; ссылки и так достаточно сложные без бесполезных синонимов.)
В отличие от этого, жёсткие ссылки больше похожи на жёсткие ссылки в файловой системе Unix: они используются для доступа к базовому объекту без учёта его (другого) имени. Когда слово «ссылка» используется без прилагательного, как в следующем абзаце, обычно имеется в виду жёсткая ссылка.
Ссылки легко использовать в Perl. Есть только одно основное правило: в общем случае Perl не выполняет неявного присвоения или де-референции ссылок. Когда скаляр содержит ссылку, он всегда ведёт себя как простой скаляр. Он не превращается магически в массив, хеш или подпрограмму; вам нужно явно сказать ему это, сделав де-референцию.
Создание ссылок
Ссылки могут быть созданы несколькими способами.
Оператор обратной косой черты
Используя оператор обратной косой черты на переменной, подпрограмме или значении. (Это работает примерно как оператор & (адрес) в C.) Это обычно создаёт ещё одну ссылку на переменную, так как в таблице символов уже есть ссылка на переменную. Но ссылка в таблице символов может исчезнуть, и у вас всё ещё будет ссылка, возвращённая оператором обратной косой черты. Вот несколько примеров:
$scalarref = \$foo;
$arrayref = \@ARGV;
$hashref = \%ENV;
$coderef = \&handler;
$globref = \*foo; Невозможно создать истинную ссылку на дескриптор ввода-вывода (дескриптор файла или дескриптор каталога) с помощью оператора обратной косой черты. Максимум, что вы можете получить, — это ссылка на 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) Автовивификация
Ссылки соответствующего типа могут появляться, если вы де-референцируете их в контексте, который предполагает их существование. Поскольку мы ещё не говорили о де-референции, мы пока не можем показать вам никаких примеров.
Слоты 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 рекурсивно. Поэтому следующее выводит «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() будут похожи. Для их создания мы назначим замыкание typeglob с именем функции, которую мы пытаемся создать.
@colors = qw(red blue green yellow orange purple violet);
for my $name (@colors) {
no strict 'refs'; # allow symbol table manipulation
*$name = *{uc $name} = sub { "<FONT COLOR='$name'>@_</FONT>" };
} Теперь все эти разные функции, похоже, существуют независимо. Вы можете вызвать red(), RED(), blue(), BLUE(), green() и т. д. Эта техника экономит время компиляции и использование памяти, а также снижает вероятность ошибок, так как проверки синтаксиса происходят во время компиляции. Важно, чтобы любые переменные в анонимном подпрограмме были лексическими, чтобы создать надлежащее замыкание. Вот причина для my в переменной итерации цикла.
Это одно из немногих мест, где предоставление прототипа замыканию имеет смысл. Если вы хотели навязать скалярный контекст аргументам этих функций (вероятно, неразумно для этого конкретного примера), вы могли бы написать это так:
*$name = sub ($) { "<FONT COLOR='$name'>$_[0]</FONT>" }; Однако, поскольку проверка прототипа происходит во время компиляции, присвоение выше происходит слишком поздно, чтобы быть полезным. Это можно решить, поместив весь цикл присвоений внутри блока BEGIN, заставив его произойти во время компиляции.
Доступ к лексическим переменным, которые изменяются со временем (например, к тем, что в цикле for выше, в основном алиасы элементов из окружающих лексических областей), работает только с анонимными подпрограммами, а не с именованными подпрограммами. Как правило, именованные подпрограммы не вложены должным образом и должны быть объявлены только в основном пространстве имён пакета.
Это происходит потому, что именованные подпрограммы создаются во время компиляции, поэтому их лексические переменные присваиваются родительским лексическим переменным от первого выполнения родительского блока. Если родительский контекст вводится второй раз, его лексические переменные создаются снова, в то время как вложенные подпрограммы всё ещё ссылаются на старые.
Анонимные подпрограммы захватывают каждый раз, когда вы выполняете оператор sub, так как они создаются на лету. Если вы привыкли использовать вложенные подпрограммы в других языках программирования со своими собственными переменными, вам придётся немного потрудиться в Perl. Интуитивное кодирование такого рода вызывает загадочные предупреждения о том, что "не будет оставаться совместно используемым" из-за причин, объяснённых выше. Например, это не сработает:
sub outer {
my $x = $_[0] + 35;
sub inner { return $x * 19 } # WRONG
return $x + inner();
} Обходной манёвр заключается в следующем:
sub outer {
my $x = $_[0] + 35;
local *inner = sub { return $x * 19 };
return $x + inner();
} Теперь inner() можно вызвать только изнутри outer(), из-за временных присвоений анонимной подпрограмме. Но когда это происходит, она имеет нормальный доступ к лексической переменной $x из контекста outer() в момент вызова outer.
Это имеет интересный эффект создания функции, локальной для другой функции, чего обычно не поддерживает Perl.
ПРЕДУПРЕЖДЕНИЕ: Не используйте ссылки в качестве ключей хэша
Вы не можете (полезно) использовать ссылку в качестве ключа хэша. Она будет преобразована в строку:
$x{ \$a } = $a; Если вы попытаетесь обратиться к ключу, это не будет жёсткая ссылка, и вы не добьётесь того, что пытаетесь сделать. Вы, возможно, хотели бы сделать что-то вроде
$r = \@a;
$x{ $r } = $r; И тогда, по крайней мере, вы можете использовать values(), которые будут реальными ссылками, вместо keys(), которые таковыми не будут.
Модуль Tie::RefHash предоставляет удобный обходной манёвр этой проблемы.
Синтаксис постфиксной разыменования
Начиная с версии v5.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.
Присвоение ссылкам
Начиная с версии v5.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, алиас будет виден только внутри этой внутренней подпрограммы и не повлияет на внешнюю подпрограмму, где переменные объявлены. Это странное поведение может измениться.
Объявление ссылки на переменную
Начиная с версии v5.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.32.0/perlref