Глава 8. Ссылки в Perl
Коротко
Ссылки — новый фундаментальный тип данных в Perl, впервые введенный для версии 5. Поведение ссылок в значительной мере напоминает поведение указателей в таких языках: как С. Как следует из названия, ссылка ссылается на элемент данных. Чтобы добраться до настоящих данных, надо разыменовать (dereference) ссылку.
С помощью ссылок можно создавать в Perl сложные структуры данных (примеры будут в главе 13). Ссылки используются для потрясающего числа приемов программирования — в частности, для создания анонимных массивов, хешей, подпрограмм и шаблонов функций (все эти темы рассматриваются в данной главе). Если подпрограмме передается более одного массива или хеша, которые при этом надо сохранить как индивидуальные структуры данных, без ссылок не обойтись. (В противном случае элементы всех массивов и хешей сливаются в один длинный список.) При создании объекта и использовании стандартной техники обращения к конструктору класса, сам конструктор, как правило, возвращает ссылку на объект, а не сам объект. Следовательно, ссылки — это еще и фундаментальный инструмент объектно-ориентированного программирования в Perl.
В Perl имеется два типа ссылок: жесткие (hard references) и символические (symbolic references). В начале главы приведен кратким обзор этих двух типов.
Жесткие ссылки
Пусть имеется переменная с именем $variable1:
$variable1 = 5;
Чтобы создать жесткую ссылку на нее, используется оператор «обратная косая черта»:
$variable1 = 5;
$reference = \$variable1;
Теперь переменная $reference содержит ссылку на $variable1 (под ссылкой понимается адрес переменной в памяти и тип переменной). Ссылки такого типа называются жесткими. Скалярные переменные Perl могут хранить жесткие ссылки в качестве значений. Чтобы разыменовать ссылку, используется оператор $ — см. следующий пример, в котором значение, на которое ссылается переменная $reference, получается с помощью разыменования через:
$variable1 = 5;
$reference = \$variable1;
print $$reference;
Разыменование ссылки позволяет получить исходные данные:
$variable1 = 5;
$reference = \$variable1;
print $$reference;
5
Что получится, если надо проверить значение ссылки? В этом случае мы увидим фактический адрес и тип переменной $variable1 в области данных интерпретатора Perl:
$variable1 = 5;
$reference = \$variable1;
print $$reference;
SCALAR(0x8a57d4)
Так выглядит жесткая ссылка в Perl.
Символические ссылки
Символическая ссылка не содержит адрес и тип элемента данных, только его имя. Вернемся к переменной $variable1 из предыдущего примера:
$variable1 = 5;
Ее имя можно присвоить другой переменной $variablename (обратите внимание, при задании имени опускается разыменовывающий префикс $):
$variable1 = 5;
$variablename = "variable1";
Разыменование имени переменной превращает это имя в ссылку на данные, хранящиеся в исходной переменной. Этот процесс и называется созданием символической ссылкой. Вот как этот метод работает с оператором $:
$variable1 = 5;
$variablename = "variable1";
print "$$variablename\n";
5
Вместо оператора $ для разыменования ссылок можно также использовать оператор-стрелку ->.
Оператор-стрелка
Оператор-стрелка — другая популярная форма оператора разыменования ссылок. Он используется со ссылками на массив, хеш, подпрограмму, и сопровожадется индексом, ключом, списком аргументов:
@array = (1, 2, 3);
$arrayreference = \@array;
print $arrayreference->[0];
1
Подробнее об операторе-стрелке рассказывается далее в этой главе.
Анонимные массивы, хеш-таблицы, подпрограммы
В Perl можно создавать массивы, хеши и подпрограммы, для доступа к которым используются ссылки, а не имена. Например, пример из предыдущего раздела сокращается, если сразу создать анонимный массив и присвоить ссылку на него переменной $arrayreference:
$arrayreference = [1, 2, 3];
Чтобы обратиться к содержимому массива, потребуется оператор разыменования:
$arrayreference = [1, 2, 3];
print $$arrayreference[0];
1
С помощью конструкций типа анонимных массивов, создаются сложные структуры данных типа массива массивов. В этой главе демонстрируются подобные приемы (а также многое другое, имеющее отношение к ссылкам). Обратимся к подробностям.
Непосредственные решения
Создание ссылки
Для создания ссылки используется оператор «обратная косая черта» (\). Ссылки, созданные с его помощью, называются жесткими. Кроме того, ссылку можно извлечь прямо из таблицы символов Perl — эта информация содержится в разделе «Как извлечь ссылку из таблицы символов» далее в этой главе. Кроме того, существуют символические ссылки — подробно о них рассказывается в разделе «Создание символических ссылок».
Оператор «обратная косая черта» может использоваться для скалярных переменных, массивов, хешей, подпрограмм или констант. Например, вот так создается жесткая ссылка на значение-константу:
$reference = \"Hello!";
Точно также создаются ссылки для переменных, массивов, хешей, подпрограмм, и т. д. (обратите внимание, что ссылки хранятся в скалярных переменных):
$scalarreference = \$myvariable;
$arrayreference = \@myarray;
$hashreference = \%myhash;
$codereference = \&mysubroutine;
$globreference = \*myname;
Чтобы получить значение переменной через ссылку, используется оператор разыменования:
$reference = \"Hello!";
print $$reference;
Hello!
Можно использовать столько уровней вложенности, сколько вам хочется. Например, в следующем примере создается ссылка на ссылку на ссылку на ссылку:
$reference4 = \\\\"Hello!";
А теперь она разыменовывается с помощью оператора $:
print $$$$$reference;
Hello!
Интересное свойство Perl состоит в том, что ссылки создаются автоматически, в момент разыменования, в предположении, что они существуют:
$$reference = 5;
print "$$reference\n";
5
Как легко заметить, в этом примере ссылка (то есть адрес в памяти) используется до того, как она реально появилась. После выполнения приведенного выше кода, ссылка действительно возникнет — этот процесс называется в Perl самооживлением (autovivification):
print "$reference\n";
SCALAR(0x8a0b14)
В следующем примере ссылки используются для передачи подпрограмме в качестве параметров двух массивов. Подпрограмма поэлементно складывает их (предполагая, что массивы имеют одинаковую длину), возвращая в качестве результата массив. Передача ссылок вместо самих массивов предотвращает объединение их содержимого в один неразличимый список:
@a = (1, 2, 3);
@b = (4, 5, 6);
sub addem
{
my ($reference1, $reference2) = @_;
for ($loop_index = 0;
$loop_index <= $#$reference1;
$loop_index++)
{
$result[$loop_index] = @$reference1[$loop_index]
+ @$reference2[$loop_index];
}
return @result;
}
@array = addem(@a, @b);
print join ’, ’, @array);
5, 7, 9
Ссылки на анонимные массивы
В начале этой главы уже создавался массив без имени, названный нами анонимным. Для этой цели использовался генератор анонимных массивов (anonymous array composer) — пара квадратных скобок:
$arrayreference = [1, 2, 3];
Эта конструкция возвращает ссылку на анонимный массив, которая присваивается скалярной переменной $arrayreference. До элементов массива можно добраться, разыменовав ссылку, но имя массива для этой цели использовать нельзя — его попросту не существует:
$arrayreference = [1, 2, 3];
print $$arrayreference[0];
1
Для разыменовывания ссылки на безымянный массив можно также использовать оператор-стрелку:
$arrayreference = [1, 2, 3];
print $arrayreference->[0];
1
Подсказка: Работа оператора-стрелки подробно описывается далее в разделе «Разыменование ссылок с помощью оператора-стрелки».
Анонимные массивы дают возможность программистам на Perl использовать полезный трюк, состоящий в интерполировании внутрь строки, ограниченной двойными кавычками, результат вызова подпрограммы или результат вычисления. В следующем примере встроенная функция Perl uc переводит в верхний регистр строку:
print "@{[uc(hello)]} there.\n";
HELLO there.
Разберемся, что делает эта конструкция. Perl обрабатывает конструкцию @{} как блок. В процессе его вычисления создается ссылка на анониманый массив из одного-единственного элемента — результата вызова функции uc. При разыменовании ссылки на анонимный массив результат интерполируется внутрь строки.
Ссылки на анонимные хеши
Можно создать ссылку на хеш без имени — он также называется анонимным. Для этой цели используется генератор анонимных хешей (anonymous hash composer) — пара фигурных скобок:
$hashreference = {
Name => Tommie,
ID => 1234,
};
Мы создали анонимный хеш, добавили в него две пары ключ/значение и поместили ссылку на хеш в скалярную переменную $hashreference. Теперь можно использовать ее как обычный хеш, но сначала надо разыменовать ссылку:
print $$hashreference{Name}
Tommie
Для этой цели может служить и оператор-стрелка:
$hashreference = {
Name => Tommie,
ID => 1234,
};
print $hashreference->{Name}
Tommie
Подсказка: Работа оператора-стрелки подробно описывается далее в разделе «Разыменование ссылок с помощью оператора-стрелки».
Ссылки на анонимные подпрограммы
Можно создать ссылку на подпрограмму без имени — такая подпрограмма называется анонимной. Для этой цели используется генератор анонимных подпрограмм (anonymous subroutine composer) — ключевое слово sub:
$codereference = sub {print "Hello!\n"};
Обратите внимание, что в конце команды пришлось добавить точку с запятой, тогда как в случае обычных подпрограмм этого делать не надо. Чтобы вызвать подпрограмму, надо разыменовать ссылку и добавить префикс &:
&$codereference;
Hello!
Также можно передать анонимной подпрограмме параметры:
$codereference = sub {print shift};
&{$codereference}("Hello!\n");
Hello!
При желании для разыменования ссылки на подпрограмму можно использовать оператор-стрелку (подробнее об этом см. раздел «Разыменование ссылок с помощью оператора-стрелки» далее в этой главе):
$codereference = sub {print shift};
$codereference->("Hello!\n");
Hello!
Как извлечь ссылку из таблицы символов
Записи в таблице символов Perl — это хеши, которые содержат адреса всех именованных элементов текущего пакета. Групповому имени name соответствует хеш *name, индексированный ключами SCALAR, HASH, CODE, и т. д. Это значит, что когда вам требуется ссылка на элемент данных, вы можете извлечь ее прямо из таблицы символов, а не использовать оператор \:
$scalarreference = *name{SCALAR};
$arrayreference = *name{ARRAY};
$hashreference = *name{HASH};
$codereference = *name{CODE};
$ioreference = *name{IO};
$globreference = *name{GLOB};
Например, чтобы получить ссылку на переменную с именем $variable1 и с ее помощью вывести значение этой переменной, можно использовать код:
$variable1 = 5;
$scalarreference = *variable1{SCALAR}
print $$scalarreference;
5
Точно так же можно получить ссылку на подпрограмму:
sub printem
{
print "Hello!\n";
}
$codereference = *printem{CODE};
&$codereference;
Hello!
Конструкция *name{IO} возвращает дескриптор потока ввода и/или вывода — то есть, дескриптор файла, сокет, дескриптор каталога.
Подсказка 1: Нельзя получить ссылку на дескриптор потока ввода/вывода с помощью оператора \.
Подсказка 2: Доступ к ссылке через записи в таблице символов зависит от того, использовался ли соответствующий элемент данных (то есть, существует ли соответствующий символ). Если символа, для которого вам нужна ссылка, не существует, вместо ссылки вы получите значение undef. Ранние версии Perl возвращали ссылку на, скажем, анонимный скаляр, если выражение вида *newscalar{SCALAR} запрашивалось до инициализации переменной $newscalar, однако для Perl версии 5 это не так.
Разыменование ссылок
Для разыменовывания ссылки (то есть доступа к данным, на которые она указывает) используется оператор $. В этой главе уже приводились примеры его работы:
$variable1 = 5;
$reference = \$variable1;
print $$reference;
5
Оператор $ используется везде, где есть идентификаторы или их наборы. Вот несколько примеров разыменования базовых типов данных Perl:
sclar = $$scalarreference;
@array = @$arrayreference;
%hash = %$hashreference;
&$codereference($argument1, $argument2);
*glob = *$globreference;
Как уже было показано, с помощью оператора $ можно также одновременно разыменовывать несколько уровней ссылок:
$reference4 = \\\\"Hello!";
print $$$$$reference;
Hello!
Кроме простых случаев, можно добавлять индекс, когда разыменованию подвергается ссылка на массив:
@array = (1, 2, 3);
$arrayreference = \@array;
print $$arrayreference[0];
1
Точно так же в случае разыменования ссылки на хеш-таблицу можно указывать ключ:
%hash = (
Name => Tommie,
ID => 1234,
);
$hashreference = \%hash;
print $$hashreference{Name};
Tommie
При разыменовывании ссылки на подпрограмму можно указать список аргументов:
sub printem
{
print shift;
}
$codereference = \&printem;
$codereference->("Hello!\n");
Hello!
Прямая ссылка иногда заменяется возвращающим ее блоком. Вот как приведенные выше примеры выглядят в случае применения блоков:
$sclar = ${$scalarreference};
@array = @{$arrayreference};
%hash = %{$hashreference};
&{$codereference}($argument1, $argument2);
*glob = *{$globreference};
И последнее. Записи таблицы символов (тип данных typeglob) могут разыменовываться точно так же, как и ссылки, поскольку они содержат ссылки на все типы данных, ассоциированные с этим именем. При разыменовании ссылки с помощью префикса всегда надо указывать, какого типа данное вы расчитываете получить на выходе. Этот же самый префикс помогает Perl понять, какой из элементов данных, ассоциированных с заданным именем, вы имеете в виду при разыменовывании типа данных typeglob. Например, в следующем коде требуется получить скалярную переменную (разыменовывающий префикс $):
$variable1 =5;
print ${variable1};
5
То же самое мы получаем, если требуется массив (разыменовывающий префикс @):
@array = (1, 2, 3);
print join(", ", @{*array});
1, 2, 3, 4
Разыменование ссылок с помощью оператора-стрелки
При работе с массивами, хешами и подпрограммами для простого разыменования ссылок можно использовать оператор-стрелку. Например, вот так оператор-стрелка работает в сочетании с ссылкой на массив:
$arrayreference = [1, 2, 3];
print $arrayreference->[0];
1
Можно также создавать массивы массивов, как в следующем примере. Здесь создается анонимный массив, состоящий из двух других анонимных массивов:
$arrayreference = [[1, 2, 3], [4, 5, 6]];
Чтобы ссылаться на элементы массива массивов, используется следующий синтаксис:
$arrayreference = [[1, 2, 3], [4, 5, 6]];
print $arrayreference->[1][1];
5
(Обратите внимание на отсутствие оператора-стрелки между двумя парами квадратных скобок. Подробнее этот вопрос рассматривается далее в разделе «Когда можно опускать оператор-стрелку».)
Подсказка: Более подробно о массивах массивов рассказывается в главе 13.
Вы можете также использовать оператор-стрелку при работе со ссылками на хеш-таблицы:
$hashreference->{key} = "This is the text.";
print $hashreference->{key};
This is the text.
(В этом примере для создания элемента, на который ссылается переменная $hashreference, мы полагаемся на процесс самооживления (autovivification), о котором уже рассказывалось выше в разделе «Создание ссылки».)
А вот как используется оператор-стрелка при ссылке на подпрограмму:
sub printem
{
print shift;
}
$codereference = \&printem;
$codereference->("Hello!\n");
Hello!
В общем случае слева от оператора-стрелки может стоять любое выражение, возвращающее ссылку:
$dataset[$today]->{prices}->[1234] = "\$4999.99";
Когда можно опускать оператор-стрелку
Оператор-стрелку в Perl необязательно ставить между квадратными и круглыми скобками. Поэтому пример из предыдущего раздела:
$dataset[$today]->{prices}->[1234] = "\$4999.99";
можно записать как:
$dataset[$today]{prices}[1234] = "\$4999.99";
Perl разрешает опускать оператор-стрелку между скобками прежде всего затем, чтобы позволить работать с массивами массивов и сделать их похожими на многомерные массивы других языков программирования. Приведем пример:
@array = ([1, 2], [3, 4]);
print $array[1][1];
4
Как определить тип ссылки с помощью оператора ref
Оператор ref помогает определить, на элемент какого типа ссылается ссылка. Синтаксис вызова ref выглядит так:
ref выражение
ref
Этот оператор возвращает значение истина (ненулевое значение), если выражение — это ссылка, и ложь в противном случае. Если не указано выражение, то оператор ref использует специальную переменную $_. Значение, возвращаемое как истина, отражает тип элемента, на который ссылается ссылка. Встроенные типы Perl соответствуют значениям:
Вот пример, в котором оператор ref применяется к ссылке на скаляр:
$variable1 = 5;
$scalarref = \$variable1;
print (ref $scalarref);
SCALAR
Создание символических ссылок
Жесткая ссылка содержит адрес элемента в памяти и его тип. Символическая ссылка вместо адреса содержит имя элемента. Тем самым последняя заставляет Perl искать переменную с заданным именем вместо того, чтобы напрямую обратиться по нужному адресу.
В следующем примере создается символическая ссылка на переменную $variable1 и затем эта ссылку используется для доступа к исходной переменной:
$variable1 = 0;
$variablename = "variable1";
$$variablename = 5;
print "$variable1\n";
5
(Обратите внимание, что символическая ссылка содержит имя переменной без разыменовывающего префикса $.)
Как и в случае жестких ссылок, использование символических ссылок с еще не существующими элементами данных приводит к созданию этих элементов. В следующем примере переменная $variable1 появилась только после того, как на нее сослались:
$variablename = "variable1";
${$variablename} = 5;
print "$variable1\n";
5
Точно так же можно создавать символические ссылки на элементы данных типа массивов или хеш-таблиц:
$arrayname = "array1";
$arrayname->[1] = 5;
print "$array1[1]\n";
5
С помощью символических ссылок можно ссылаться даже на подпрограммы:
$subroutinename ="subroutine1";
sub subroutine1
{
print "Hello!\n";
}
&$subroutinename();
Hello!
С помощью символических ссылок можно ссылаться на глобальные и локальные переменные текущего пакета. Переменные с лексической областью видимости (то есть, описанные с ключевым словом my) не содержатся в таблице символов, и поэтому на них нельзя ссылаться с помощью символических ссылок. Например, в следующем фрагменте кода вместо значения переменной, на которую указывает символическая ссылка, выводится пустая строка (Perl не может найти переменную и интерпретирует ее значение как undef):
my $variable1 = 10;
$variablename = "variable1"; # Будет проблема
print "The value is $$variablename\n";
# Приведенный выше код выдаст неполный результат:
The value is
Запрет символических ссылок
Очень сложно использовать жесткие ссылки невольно, но вполне возможно использовать символическую ссылку (то есть, имя переменной, на которую ссылаются) в результате ошибки. Чтобы запретить использование символических ссылок, следует включить в сценарий прагму:
use strict ’refs’
До окончания блока с такой прагмой Perl разрешает использовать только жесткие ссылки. Однако, если вы хотите разрешить символические ссылки внутри вложенного блока, задайте другую прагму:
no strict ’refs’
Использование ссылок на массивы как ссылок на хеши
Начиная с версии Perl 5.005 там, где должна находиться ссылка на хеш, разрешается использовать ссылку на массивы (по крайней мере, это можно делать в большинстве случаев). Иными словами, при определенной организации массива к его элементам можно обращаться по именам.
Подсказка: Это новое и экспериментальное свойство Perl. Оно может измениться в будущем.
Чтобы использовать ссылку на массив в качестве ссылки на хеш, необходимо добавить в массив дополнительную информацию: установить соответствие между ключами хеша и индексами массива. Эта информация заносится в нулевой элемент массива в следующем формате:
{key1 => index1, key2 => index2, key3 => index3, ...}
В следующем примере создается ссылка на анонимый массив, к которому добавлена информация о хеш-ключах с именами first и second:
$arrayref = [{first=>1, second=>2}, "Hello", "there"];
Теперь на элементы массива можно ссылаться с помощью ключей, что иллюстрирует следующий пример:
$arrayref = [{first=>1, second=>2}, "Hello", "there"];
print $arrayref->{first} . " " . $arrayref->{second};
Hello there
Как создать замыкание области видимости в устойчивую форму
Замыкание (closure) — анонимная подпрограмма, имеющая доступ к переменным, лексическая область видимости которых входила в область видимости подпрограммы в момент ее компиляции. Такая подпрограмма сохраняет нужные переменные в своей области видимости (а именно, не дает уничтожить их при автоматической сборке мусора), даже если вызывается позднее. Замыкания позволяют передавать подпрограмме данные в момент ее определения так, чтобы надлежащим образом инициализировать внутренние переменные.
Рассмотрим пример, который поможет понять, о чем идет речь. В следующем фрагменте кода создается подпрограмма printem, которая возвращает ссылку на анонимную подпрограмму. При вызове последняя получает доступ к строке, исходно находившейся внутри printem, даже если эта строка давно вышла из области видимости. Тем самым анонимная подпрограмма печатает как текст, переданный ей в качестве параметра, так и текст, изначально передававшийся printem:
sub printem
{
my $string1 = shift;
return sub {my $string2 = shift;
print "$string1 $string2\n";};
}
Теперь в строку $string1 подпрограммы printem заносится текст «Hello», а ссылка на анонимную подпрограмму записывается в переменную $hello:
$hello = printem("Hello");
В результате при вызове анонимной подпрограммы с некоей строкой в качестве аргумента, та сохраняет доступ также и к исходной строке и может вывести обе:
&$hello("today.");
&$hello("there.");
Hello today.
Hello there.
Таким способом подпрограмма инициализиуется до начала работы с ней. Однако подобные замыкания возможны только для переменных с лексической областью видимости. В следующем разделе приводится дополнительная информация о замыканиях.
Создание функций на основе шаблонов
Замыкания можно использовать для создания шаблонов функций, позволяющих создавать и настраивать пользовательские функции в процессе работы сценария.
Рассмотрим пример. Мы будем использовать шаблон для создания трех новых функций — printHello, printHi и printGreetings. Они будут выводить, соответственно, строки «Hello», «hi» и «Greetings». Начнем с того, что запомним эти строки в массиве:
@gretings = ("Hello", "Hi", "Greetings");
Далее, мы выполним цикл foreach по элементам массива с использованием лексической переменной в качестве индекса (чтобы использовать замыкания, необходимы лексические переменные — см. предыдущий раздел). На каждой итерации цикла создается непоименованная функция, использующая очередной элемент массива @greetings, и запись таблицы символов (тип данных typeglob) для этой функции:
foreach my $term (@greetings) {
*{"print" . $term} = sub {print "$term\n"};
}
Теперь можно вызывать созданные на основе шаблона пользовательские функции printHello, printHi и printGreetings:
printHello();
printHi();
printGreetings();
Hello
Hi
Greetings
Так и работают шаблоны функций. Если бы мы просто запомнили ссылки на анонимные подпрограммы в подходящих скалярных переменных:
foreach my $term (@greetings) {
${"print" . $term} = sub {print "$term\n"};
}
то могли вызывать эти подпрограммы через разыменовывания ссылок, а не как «истинные» подпрограммы:
&$printHello();
&$printHi();
&$printGreetings();
Hello
Hi
Greetings