Виды ссылок
Данные, используемые программой, размещаются в оперативной памяти компьютера. Каждая переменная имеет свой адрес и свое значение, которое хранится по этому адресу. Адрес переменной является информацией, которую также можно использовать в программе.
Ссылка на некоторую переменную содержит адрес этой переменной в оперативной памяти. Говорят, что ссылка указывает на переменную. Ссылки широко используются в современных языках программирования, таких как Pascal, C/C++. Вместо слова "ссылка" для обозначения термина может применяться слово "указатель". Основной областью применения ссылок является создание сложных структур данных, способных изменяться во время выполнения программы. Для ссылок используются специальные обозначения. В языке С это символ "*" перед именем переменной. В языке Pascal существует специальный тип данных для описания ссылок-переменных. Признаком этого типа является символ "^ " перед идентификатором, описывающим базовый тип данных. Ссылка может быть переменной или константой.
Ссылка в языке Perl — это обычная скалярная величина, в которой хранится некоторый адрес в оперативной памяти.
Ссылка в языке Perl может указывать
на любой фрагмент данных. Фрагментом данных здесь мы называем любую переменную,
константу или часть кода программы. Тип ссылки определяется типом данных, на
которые она указывает. Таким образом, существуют следующие типы ссылок: ссылка на скалярную величину, ссылка на массив, ссылка на хеш, ссылка на функцию. Нельзя использовать ссылку одного типа там, где контекст выражения требует присутствия ссылки другого типа, например, использовать ссылку на массив вместо ссылки на хеш-массив. Поскольку сама ссылка является скалярной величиной, то, естественно, существует ссылка на ссылку. Имеется еще один вид ссылок, который мы в свое время рассмотрим подробнее. Это ссылки на данные типа typegiob. Тип typeglob является внутренним типом языка Perl, который служит для обозначения переменных разных типов, имеющих общее имя. Принадлежность к типу typeglob, обозначается префиксом "*". Например, запись *abc обозначает всю совокупность, а также любую из следующих переменных: скаляр $abc, массив @аЬс, хеш %abc. В данной главе мы не будем рассматривать этот вид ссылок. Отметим только, что он лежит в основе механизма экспорта/импорта модулей.
(Работа с модулями обсуждается в части 12.)
Тип ссылки можно определить при помощи встроенной функции refo, которая рассматривает свой аргумент как ссылку и возвращает символическое обозначение ее типа. Если аргумент не является ссылкой, возвращается пустая строка. Встроенные типы обозначаются следующим образом:
REF ссылка на ссылку;
SCALAR ссылка на скаляр;
ARRAY ссылка на массив;
HASH ссылка на ассоциативный массив;
CODE ссылка на подпрограмму;
GLOB ссылка на переменную типа typegiob.
Ссылки Е языке Perl бывают жесткие и символические. Понятия "жесткая ссылка" и "символическая ссылка" вместе с названиями проникли в Perl из мира UNIX, где они используются применительно к файловой системе. Разберем их применение в UNIX, чтобы лучше понимать, для чего они нужны в Perl.
Каждому файлу в UNIX соответствует индексный дескриптор - структура данных, имеющая определенный формат, расположенная в специально отведенной области диска и содержащая важнейшую информацию о файле: тип файла, его расположение на диске, права доступа и т. д. Каждый дескриптор имеет числовой номер, соответствующий его положению в таблице индексных дескрипторов. Этот номер и является внутренним именем файла для операционной системы. Для нее сущность файла заключается в его индексном дескрипторе, а не в его содержимом. Для пользователя, напротив, важно содержимое файла, а о существовании индексного дескриптора он может даже не подозревать. Кроме того, пользователю удобнее работать с именем файла, а не с числовым номером. Для удобства пользователя создается ссылка на файл, которая ставит в соответствие индексному дескриптору имя файла. Ссылка представляет собой запись в каталоге, который является тоже файлом, выполняющим специальную функцию регистрации других файлов. В простейшем случае эта запись содержит два поля: имя файла и номер индексного дескриптора. Можно создать несколько ссылок с разными именами в одном или нескольких каталогах, указывающих на один файл. Ссылка указывает на индексный дескриптор, но для краткости говорят о ссылке на файл. Следует подчеркнуть, что все ссылки равноправны. Ссылка, созданная первой, не имеет никакого преимущества перед ссылками, созданными позднее. В индексном дескрипторе среди другой важной информации содержится счетчик ссылок. Удаление из каталога ссылки на файл уменьшает значение счетчика ссылок на единицу. Когда значение счетчика ссылок становится равным нулю, файл удаляется, а его индексный дескриптор освобождается для использования новым файлом.
Рассмотренные ссылки называются жесткими ссылками. Кроме них, существуют символические ссылки. Символическая ссылка является не просто записью в каталоге, а файлом особого типа, содержащим символьное имя другого файла. В качестве файла символьная ссылка имеет свой индексный дескриптор. Поскольку символическая ссылка является самостоятельным файлом, ее существование никак не отмечается в дескрипторе того файла, на который она указывает. В частности, если удалить все символические ссылки на файл, то это не будет означать его удаление.
Жесткие и символические ссылки в языке Perl напоминают одноименные понятия в файловой системе UNIX. Жесткая ссылка в Perl — это скалярная величина, которая содержит адрес некоторой области памяти, являющейся носителем данных. Сами данные будем называть субъектом ссылки. Символическая ссылка — это скалярная переменная, которая содержит имя другой скалярной переменной. "Истинной" ссылкой является жесткая ссылка. Она создается одним из способов, перечисленных в разделе 9.1. Внутренняя организация жестких ссылок такова, что для каждого субъекта ссылки поддерживается счетчик ссылок. Область памяти, занимаемая субъектом ссылки, освобождается, когда значение счетчика ссылок становится равным нулю. В большинстве случаев мы имеем дело с жесткими ссылками, а использование символических ссылок будем специально оговаривать.
Главным применением ссылок в языке Perl является создание сложных структур данных. Мы знаем, что основными типами данных в Perl являются скаляры, массивы и хеш-массивы. Многомерные массивы или более сложные структуры данных, аналогичные записям языка Pascal или структурам языка С, в языке Perl отсутствуют. В более ранних версиях языка отсутствовала и возможность создания сложных структур данных на основе имеющихся типов. Такая возможность появилась в версии Perl 5.0 вместе с появлением ссылок. В практике программирования часто встречаются данные, которые удобно представлять, например, в виде двумерных массивов, реже трехмерных массивов или других подобных структур. Двумерный массив можно рассматривать как одномерный массив, элементами которого являются также одномерные массивы. Возможность такого представления есть во многих языках программирования. В языке Perl невозможно создать массив с массивами в качестве элементов. То же самое относится и к хешам. Элементом массива или хеша может быть только скалярная величина. Поскольку ссылка является скалярной величиной, можно создать массив или хеш, элементами которого являются ссылки на массивы или хеши, и таким образом получить структуру, которую можно использовать как массив маесивов (соответственно массив хешей, хеш массивов, хеш хешей). Благодаря ссылкам можно на основе массивов и хешей конструировать структуры данных произвольной сложности.
Помимо создания сложных структур данных, ссылки активно применяются для работы с объектами. Слово "объект" здесь обозначает основное понятие объектно-ориентированного подхода к программированию. (Объекты рассматриваются в части 13.)
В этой главе мы рассмотрим основное применение ссылок как средства для конструирования структур данных. Другие применения будут рассмотрены в соответствующих главах.
Создание ссылок
Существует несколько способов порождения ссылок. Рассмотрим их в порядке следования от чаще употребляемых синтаксических конструкций к более редким.
Операция ссылки "\"
Операция "\", примененная к единственному аргументу, создает ссылку на этот аргумент. В качестве последнего может выступать переменная любого типа или константа. Примеры:
$а=\5;
$scal_ref=$a; $arr_ref=\@myarray; $hash_ref=\%myhash; $funC_ref=\ anyfunc ;
В данном примере скалярной переменной $а присваивается значение ссылки на константу 5, т. е. адрес ячейки памяти, в которой хранится число 5. Адрес самой переменной $а хранится в переменной $scai_ref. Переменные $arr_ref, $hash_ref, $func_ref хранят адреса ячеек памяти, являющихся начальными точками размещения соответственно массива gmyarray, хеш-массива %myhash и кода функции myfunc. К переменным, содержащим ссылки, можно применять все операции допустимые для скалярных величин. Их можно присваивать другим переменным, складывать, умножать, делить, выводить на экран и т. д. За исключением присваивания применение подобных операций к ссылкам, как правило, смысла не имеет. Например, вывод рассмотренных выше переменных
print $scal_ref, "\n", $arr_ref,"\n", $hash_ref, "\n", $func_ref, "\n"; будет состоять иа строк, подобных следующим:
SCALAR(Ox9b8994) ARRAY(Ох9Ь8а18) HASH(Ox9b8a60) CODE(Ox9b3dl4)
Здесь каждая строка содержит слово, обозначающее тип ссылки и ее значение — адрес в виде шестнадцатеричного числа.
Операция, которую действительно имеет смысл применять к ссылкам, это операция разыменования, то есть операция получения того значения, на которое указывает ссылка. Синтаксические конструкции, используемые для разыменования ссылок, мы рассмотрим после того, как обсудим способы их создания.
Конструктор анонимного массива
В рассмотренном выше примере операция "\" применялась к переменным, обладающим именами. Perl позволяет создавать ссылки на анонимные массивы при помощи специальной конструкции, использующей квадратные скобки:
$arr_ref = [1,2,3];
В результате данной операции присваивания будет создан анонимный массив с элементами (1,2,3), а переменной $arr_ref будет присвоено значение ссылки на этот массив.
Компилятор различает случаи использования квадратных скобок для создания ссылки на анонимный массив и для обращения к отдельным элементам массива, как, например, в операции присваивания $а = $myarray[2].
Замечание |
|
Свободный синтаксис языка Perl допускает существование конструкций, смысл которых не очевиден. К рассматриваемой теме имеет отношение следующий пример. Формально выражение \($а, $Ь, $с) представляет собой анонимный массив из трех элементов ($а, $ь, $с), к которому применяется операция ссылки "\". Означает ли это, что значением выражения является ссылка на анонимный массив? Нет, это просто сокращенная запись массива, состоящего из трех элементов-ссылок (\$а, \$Ь, \$с), а для создания ссылки на анонимный массив существует единственный способ, рассмотренный выше. |
Конструктор анонимного ассоциативного массива
По аналогии с массивами можно создавать ссылки на анонимные ассоциативные массивы, используя фигурные скобки. Операция присваивания
%hash_ref = {
'Опе'=>1,
'Two'=>2,
"Three'=>3 };
создаст анонимный хеш-массив cone'=>i, 'Two'=>2, "rhree'=>3) и присвоит переменной %hash_ref значение ссылки на этот хеш.
Фигурные скобки используются во многих конструкциях, например, для обращения к индивидуальному элементу хеш-массива
$а = $myhash{"first"}
или для выделения блока операторов. Обычно такие случаи легко различимы, и их нельзя спутать с порождением ссылки на анонимный хеш. Но иногда возникают неоднозначные ситуации, требующие разрешения. Забегая вперед, приведем пример, связанный с определением функции пользователем.
(Желающие могут предварительно прочитать начало части 11, в которой рассказывается о подпрограммах и функциях.)
Предположим, что необходимо определить функцию, которая создает анонимный хеш и возвращает ссылку на него. Возвращаемое значение можно задать при помощи встроенной функции return. Если конструкция return отсутствует, то в качестве возвращаемого значения по умолчанию принимается значение последнего выражения, вычисленного внутри функции. Таким образом, синтаксически допустимо следующее определение функции
sub get_hash_ref { {@_} }
В данном примере внутренняя конструкция в фигурных скобках интерпретируется как блок. Для того чтобы она интерпретировалась как ссылка на анонимный хеш, необходимо использовать функцию return или поставить перед внутренней конструкцией знак "+":
sub get_hash_ref { return {(?_}
} sub get_hash_ref { +{@_} }
Другие способы
В предыдущих разделах рассмотрены основные способы создания ссылок: . О применение операции " \ " к объекту ссылки;
специальные конструкции [ ] и { }, создающие в определенном контексте ссылку соответственно на анонимный массив и анонимный ассоциативный массив. Эти способы применяются наиболее
часто в тех случаях, когда возникает необходимость в использовании ссылок. Но
существуют и другие источники появления ссылок, о которых следует упомянуть
для полноты изложения.
Конструктор анонимной подпрограммы
Мы уже использовали в примерах подпрограммы, не дожидаясь их систематического изучения. Поэтому можем рассмотреть в этой главе такой вид ссылки, как ссылка на анонимную подпрограмму.
Ссылка на анонимную подпрограмму может быть создана при помощи ключевого слова sub, за которым следует блок — последовательность операторов, заключенная в фигурные скобки:
$sub_ref = sub { print "Привет!\n");
В результате операции присваивания в переменную $sub_ref заносится адрес, по которому размещается код анонимной подпрограммы. В данном примере подпрограмма состоит из единственного обращения к функции print, выводящей строку "Привет!".
Пример, иллюстрирующий данный вид ссылки, будет рассмотрен далее в этой главе.
Ссылка, создаваемая конструктором объекта
В версию 5.0 языка Perl была добавлена поддержка объектно-ориентированного программирования. Основой объектно-ориентированного подхода являются понятия класс и объект.
(Классы и объекты рассматриваются в части 13.)
Понятие "объект" реализовано
в языке Perl таким образом, что объект становится доступным в программе только
через ссылку на него. Для создания объекта используется специальная подпрограмма
— конструктор, которая, в свою очередь, применяет для этого встроенную
функцию bless о. Конструктор возвращает ссылку на объект. Таким образом, это
еще один способ порождения ссылок, без которого не обойтись тем, кто использует
объектно-ориентированный подход в Perl.
Ссылки на данные типа typeglob
Компилятор Perl хранит имена всех переменных программы в таблице символов. Отдельная таблица символов существует для каждого пакета, образуя собственное пространство имен.
(О пакетах и таблицах символов рассказывается в части 12.)
Каждый идентификатор, встречающийся в пакете, заносится в таблицу символов. Одинаковые идентификаторы, соответствующие переменным разных типов, образуют гнездо, в котором каждому типу соответствует свой элемент, содержащий адрес переменной данного типа. Если, например, в программе имеются следующие строки
$а=5;
@а=(1,2,3,4,5);
%а=("опе"=>1,"two"=>2,"three"=>3);
sub a {return "Hello, Mike!";};
то таблица символов содержит гнездо для идентификатора "а", состоящее из четырех элементов, хранящих адреса: скалярной переменной $а, массива @а, ассоциативного массива %а и кода подпрограммы &а.
В языке Perl существует внутренний тип данных typeglob. Признаком этого типа является наличие префикса "*" в имени переменной. Тип typeglob служит для ссылки на все переменные разных типов с одинаковыми именами. Например, переменная *а обозначает ссылку на гнездо "а" в таблице символов. Используя специальную запись, можно при помощи переменной typeglob получить ссылки на отдельные элементы гнезда: $scalarref = *a{SCALAR}; # эквивалентно $scalarref = \$a; $arrayref = *a{ARRAY}; # эквивалентно $arrayref = \@а; $hashref = *a{HASH}; # эквивалентно $hashref = \%a; $coderef = *a{CODE}; # эквивалентно $coderef = \&a; $globref = *a{GLOB}; # эквивалентно $globref = \*a;
Неявное создание ссылок
В рассмотренных случаях осуществляется явное создание ссылки при помощи операции "\" или специальных синтаксических конструкций. В них всегда явным образом определяется скалярная переменная, в которую и заносится значение ссылки. Ссылка может также создаваться неявно в случае, когда операция разыменования применяется к ссылке, ранее не созданной в программе явным образом, и в контексте выражения предполагается, что такая ссылка должна существовать. Возможно, последнее предложение звучит не совсем понятно. Его смысл станет ясным в следующем разделе.
Разыменование ссылокРазыменованием ссылки называется получение объекта, на который указывает эта ссылка. Для разыменования, как и для создания ссылки, применяются различные синтаксические конструкции, подчас достаточно сложные для визуального восприятия. К ним нужно привыкнуть. Вид конструкции зависит от типа ссылки, к которой применяется разыменование. Рассмотрим их по степени возрастания сложности. Разыменование простой скалярной переменной Если ссылка на некоторый объект: скалярную переменную, массив, ассоциативный массив и т. д., является простой скалярной переменной без индексов, то для обращения к самому объекту применяется правило: вместо имени объекта подставить в выражение простую скалярную переменную, содержащую ссылку. Например: 1 $а = $$scal_ref; 2 @b = @$arr_ref; 3 %с = %$hash_ref; 4 &f - &$code_ref; 5 $$d[0] = 7; 6 $$h{"one"} = 1; Здесь предполагается, что переменная $scal_ref содержит ссылку на скалярную величину, $arr_ref - ссылку на массив, $hash_ref - ссылку на ассоциативный массив, $code_ref — ссылку на подпрограмму. Рассмотрим подробно пятую строку. Во-первых, следует определить, что является ссылкой: скалярная переменная $d, указывающая на анонимный массив, или элемент $d[0] массива @d. Ответ содержится в сформулированном выше правиле разыменования. Поскольку в строке 5 применяется именно оно, то индексированная переменная $d[0] ссылкой быть не может. Ссылкой является простая скалярная переменная $d, которая используется в качестве имени. Из контекста видно, что на ее месте должно стоять имя массива, следовательно, $d является ссылкой на анонимный массив Во-вторых, здесь мы имеем пример неявного создания ссылки, о котором говорилось в предыдущем разделе. Ссылка $d не была ранее создана явным образом, но ее существование предполагается в операции присваивания. Поэтому компилятор создаст ссылку $d на анонимный массив, поместит в нее адрес массива и по этому адресу сохранит значение первого элемента, равное 7. Все сказанное можно отнести к шестой строке с единственным отличием: вместо ссылки на анонимный массив здесь фигурирует ссылка $ь на анонимный хеш-массив. Блоки в операциях разыменования ссылокЕсли ссылка является не простой скалярной переменной, а, например, элементом массива или ассоциативного массива, то для ее разыменования нельзя применить правило предыдущего раздела. В этом случае следует заключить ссылку в фигурные скобки и полученный блок использовать в качестве имени переменной в выражениях. Вообще, во всех случаях разыменования ссылок в качестве имени объекта можно использовать блок, результатом выполнения которого является ссылка соответствующего типа. ${$d[0]} = 7; ${$h{"one"}} = 1; ${&f()}[l] = 3; Разберем первую строку. Начальный
символ $ является признаком скалярной переменной, за которым должно следовать
ее имя. Вместо имени используется блок, следовательно, выражение внутри блока
интерретируется как ссылка. В данном случае осуществляется разыменование ссылки
$d[0], являющейся элементом массива @а. Аналогично, во второй строке осуществляется
обращение к скалярной переменной, на которую указывает ссылка $h{"one"},
являющаяся элементом ассоциативного массива %ь. В третьей строке блок, возвращающий
ссылку, состоит из одного обращения к функции f (). Ее значение интерпретируется
как ссылка на массив, и второму элементу этого массива присваивается значение
3. Применение правила разыменования предыдущего раздела может привести к появлению громоздких выражений, содержащих множество вложенных друг в друга блоков, и очень сложных для визуального восприятия. Непростыми являются уже вышеприведенные примеры. При построении же более сложных структур выражения становятся почти необозримыми. Даже достаточно простые конструкции требуют определенного усилия для того, чтобы понять, что они означают: $<$а[0]'}[1] = 17; , ${$Ь[0]}{"опе"} = 1; В первой строке осуществляется обращение к отдельному элементу массива массивов, во второй — к отдельному элементу массива хеш-массивов.
Несколько упростить запись и улучшить наглядность можно, используя операцию "->" ("стрелка"). Аргумент слева от стрелки может быть любым выражением, возвращающим ссылку на массив или хеш-массив. Если левосторонний аргумент является ссылкой на массив, то аргумент справа от стрелки — индекс, заключенный в квадратные скобки и определяющий элемент этого массива. Если левосторонний аргумент является ссылкой на хеш-массив, то аргумент справа от стрелки — значение ключа, помещенное в фигурные скобки и определяющее элемент этого хеш-массива. Результатом операции "->" является соответственно значение элемента массива или хеш-массива. Предыдущий пример можно более компактно записать в виде $а[0]->[1] = 17; $Ь[0]->{"опе"} = 1; Конструкция $а[0]->[1] обозначает второй элемент массива, определяемого ссылкой $а[0]. Конструкция $ь[0]->{"опе"} обозначает элемент, соответствующий ключу "one" хеш-массива, задаваемого ссылкой $Ь[0]. Вообще, если $arr_ref — ссылка на массив, то $arr_ref->[$i] обозначает i-й элемент этого массива. Если $hash_ref — ссылка на хеш-массив, то $hash_ref->{"key"} обозначает элемент этого хеш-массива, соответствующий ключу "key". Если бы в последнем примере вместо именованных массивов @а и @ь использовались ссылки на массив, например, $ref_a и $ref_b, то соответствующие операции присваивания имели вид $ref_a->[0]->[l] = 17; $ref_b->[0]->{"one"} = 1; Здесь мы снова сталкиваемся с неявным созданием ссылок. По контексту элемент массива $ref_a->[0] должен быть ссылкой на массив, а $ref_b->[0] — ссылкой на хеш-массив. Обе ссылки ранее не были определены, но их существование предполагается в контексте выражения. Данные ссылки будут созданы автоматически. Операция "->" позволяет для обращения к отдельному элементу составного массива или хеш-массива использовать более простые выражения, например, $a[$i]->[$j]->[$k] вместо ${${$a[$i]}[$j]}[$k], $b[$i]->{"key"}->[$j] вместо ${${$b[0]}{"key"}}[$j] и т. д. Дальнейшее упрощение связано с тем, что при обращении к элементам гожных структур, представляющих собой комбинации вложенных массивов и хеш-массивов, можно опустить символы "->" между квадратными и/или фигурными скобками, содержащими индексы или ключи элементов. Предыдущие выражения примут еще более простой вид: $a[$i][$j][.$k] и $b[$i] {"key"} [$j]соответственно.
|