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()) являются исполняемыми операторами, а не объявлениями на этапе компиляции.
Поскольку фигурные скобки используются для нескольких других вещей, включая BLOCK, вам иногда придётся сделать различие между фигурными скобками в начале оператора, поместив + или return перед ними, чтобы Perl понимал, что открывающая фигурная скобка не является началом BLOCK. Экономичность и запоминаемость использования фигурных скобок считаются оправданными этими случайными дополнительными трудностями.
Например, если вам нужна функция для создания нового хэша и возвращения ссылки на него, у вас есть такие варианты:
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.
Это действие на расстоянии может быть запутанным, поэтому будьте осторожны при использовании 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"; всегда означало вывести «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.
Синтаксис постфиксной дескрипции
Начиная с версии 5.20.0, доступен постфиксный синтаксис для использования ссылок. Он ведет себя так, как описано в "Использование ссылок", но вместо префиксного знака используется постфиксный знак с звездочкой.
Например:
$r = \@a;
@b = $r->@*; # equivalent to @$r or @{ $r }
$r = [ 1, [ 2, 3 ], 4 ];
$r->[1]->@*; # equivalent to @{ $r->[1] } В Perl 5.20 и 5.22 этот синтаксис должен быть включен с помощью use feature 'postderef'. Начиная с Perl 5.24, для его использования не требуются объявления функций.
Постфиксная дескрипция должна работать во всех случаях, где работала блочная (циркумфиксная) дескрипция, и должна быть полностью эквивалентна. Определены следующие эквивалентности:
$sref->$*; # same as ${ $sref }
$aref->@*; # same as @{ $aref }
$aref->$#*; # same as $#{ $aref }
$href->%*; # same as %{ $href }
$cref->&*; # same as &{ $cref }
$gref->**; # same as *{ $gref } Обратите особое внимание, что $cref->&* не эквивалентно $cref->(), и может служить различным целям.
Элементы глобальных переменных могут быть извлечены с помощью функции постфиксной дескрипции:
$gref->*{SCALAR}; # same as *{ $gref }{SCALAR} Постфиксная дескрипция массива и скаляра может использоваться в интерполируемых строках (двойные кавычки или оператор qq), но только если функция postderef_qq включена.
Постфиксное срезание ссылок
Срезы значений массивов и хэшей также могут быть взяты с использованием постфиксного синтаксиса дескрипции со следующими эквивалентностями:
$aref->@[ ... ]; # same as @$aref[ ... ]
$href->@{ ... }; # same as @$href{ ... } Постфиксное срезание пар "ключ/значение", добавленное в 5.20.0 и описанное в разделе разделе "Срезы хэшей с ключами/значениями" в perldata, также работает как ожидается:
$aref->%[ ... ]; # same as %$aref[ ... ]
$href->%{ ... }; # same as %$href{ ... } Как и в случае с постфиксным массивом, постфиксное срезание значений может использоваться в интерполируемых строках (двойные кавычки или оператор qq), но только если функция postderef_qq feature включена.
Присваивание ссылкам
Начиная с версии 5.22.0, оператор ссылки может быть присвоен. Он выполняет операцию алиасирования, так что имя переменной, на которую ссылается левая часть, становится псевдонимом для того, на что ссылается правая часть:
\$a = \$b; # $a and $b now point to the same scalar
\&foo = \&bar; # foo() now means bar() Этот синтаксис должен быть включен с помощью use feature 'refaliasing'. Он экспериментальный и по умолчанию будет предупреждать, если no warnings 'experimental::refaliasing' не включено.
Эти формы можно присваивать, и правая часть будет вычислена в скалярном контексте:
\$scalar
\@array
\%hash
\&sub
\my $scalar
\my @array
\my %hash
\state $scalar # or @array, etc.
\our $scalar # etc.
\local $scalar # etc.
\local our $scalar # etc.
\$some_array[$index]
\$some_hash{$key}
\local $some_array[$index]
\local $some_hash{$key}
condition ? \$this : \$that[0] # etc. Операции среза и скобки вычисляют правую часть в списочном контексте:
\@array[5..7]
(\@array[5..7])
\(@array[5..7])
\@hash{'foo','bar'}
(\@hash{'foo','bar'})
\(@hash{'foo','bar'})
(\$scalar)
\($scalar)
\(my $scalar)
\my($scalar)
(\@array)
(\%hash)
(\&sub)
\(&sub)
\($foo, @bar, %baz)
(\$foo, \@bar, \%baz) Каждый элемент в правой части должен быть ссылкой на данные правильного типа. Скобки, непосредственно окружающие массив (и, возможно, также my/state/our/local) сделают каждый элемент массива псевдонимом соответствующего скаляра, на который ссылается правая часть:
\(@a) = \(@b); # @a and @b now have the same elements
\my(@a) = \(@b); # likewise
\(my @a) = \(@b); # likewise
push @a, 3; # but now @a has an extra element that @b lacks
\(@a) = (\$a, \$b, \$c); # @a now contains $a, $b, and $c Сочетание этой формы с local и размещение скобок непосредственно вокруг хэша запрещено (потому что не ясно, что они должны делать):
\local(@array) = foo(); # WRONG
\(%hash) = bar(); # WRONG Присваивание ссылкам и не-ссылкам может быть комбинировано в списках и условных выражениях "тернарный оператор", при условии, что значения в правой части соответствуют типу каждого элемента в левой, хотя это может привести к запутанному коду:
(my $tom, \my $dick, \my @harry) = (\1, \2, [1..3]);
# $tom is now \1
# $dick is now 2 (read-only)
# @harry is (1,2,3)
my $type = ref $thingy;
($type ? $type eq 'ARRAY' ? \@foo : \$bar : $baz) = $thingy; Цикл foreach также может принимать конструктор ссылки для своей переменной цикла, хотя синтаксис ограничен одним из следующих вариантов с необязательным my, state, или our после обратной косой черты:
\$s
\@a
\%h
\&c Скобки не допускаются. Эта функция особенно полезна для массивов-из-массивов или массивов-из-хэшей:
foreach \my @a (@array_of_arrays) {
frobnicate($a[0], $a[-1]);
}
foreach \my %h (@array_of_hashes) {
$h{gelastic}++ if $h{type} eq 'funny';
} ПРЕДУПРЕЖДЕНИЕ: Алиасирование не работает правильно с замыканиями. Если вы попытаетесь алиасить лексические переменные из вложенной подпрограммы или eval, алиасирование будет видно только внутри этой вложенной подпрограммы и не повлияет на внешнюю подпрограмму, где переменные объявлены. Это странное поведение может быть изменено.
Объявление ссылки на переменную
Начиная с версии 5.26.0, оператор ссылки может следовать за my, state, our, или local. Этот синтаксис должен быть включен с помощью use feature 'declared_refs'. Он экспериментальный и по умолчанию будет предупреждать, если no warnings 'experimental::refaliasing' не включено.
Эта функция делает следующие:
my \$x;
our \$y; эквивалентными:
\my $x;
\our $x; Она предназначена в основном для использования в присваиваниях ссылкам (см. "Присваивание ссылкам" выше). Она также позволяет использовать обратную косую черту только для некоторых элементов в списке объявленных переменных:
my ($foo, \@bar, \%baz); # equivalent to: my $foo, \my(@bar, %baz); ПРЕДУПРЕЖДЕНИЕ: Не используйте ссылки в качестве ключей хэша
Вы не можете (полезно) использовать ссылку в качестве ключа хэша. Она будет преобразована в строку:
$x{ \$a } = $a; Если вы попытаетесь дескрибировать ключ, он не выполнит жесткую дескрипцию, и вы не добьетесь того, что пытаетесь сделать. Возможно, вам нужно сделать что-то вроде
$r = \@a;
$x{ $r } = $r; И тогда, по крайней мере, вы можете использовать значения(), которые будут реальными ссылками, вместо ключей(), которые не будут.
Модуль Tie::RefHash предоставляет удобный обходной метод для этого.
См. также
Помимо очевидных документов, исходный код может быть познавательным. Некоторые патологические примеры использования ссылок можно найти в файле t/op/ref.t теста регрессии в директории исходного кода Perl.
См. также perldsc и perllol для того, как использовать ссылки для создания сложных структур данных, и perlootut и perlobj для того, как использовать их для создания объектов.
© 1993–2021 Larry Wall and others
Licensed under the GNU General Public License version 1 or later, or the Artistic License.
The Perl logo is a trademark of the Perl Foundation.
https://perldoc.perl.org/5.34.0/perlref