HTML page

Глава 11 Ссылки и записи

Ссылки и записи

Введение

В Perl существуют три основных типа данных: скалярные величины, массивы и хэши. Конечно, многие программы удается написать и без сложных структур данных, но обычно простых переменных и списков все же оказывается недостаточно. Три встроенные структуры данных Perl в сочетании со ссылками позволяю! строить сколь угодно сложные и функциональные структуры данных - те самыг записи, которых так отчаянно не хватало в ранних версиях Perl. Правильно выбирая структуру данных и алгоритм, вы иногда выбираете между элегантпои программой, которая быстро справляется со своей задачей, и убогой поделкой, ра ботающей с черепашьей скоростью и нещадно пожирающей системные ресурсы. Первая часть этой главы посвящена созданию и использованию простых ссылок. Во второй части рассказывается о применении ссылок для создания структур данных более высокого порядка.

Ссылки

Чтобы хорошо понять концепцию ссылок, сначала необходимо разобраться с тем. как в Perl хранятся значения переменных. С любой определенной переменной ассоциируется имя и адрес области памяти. Идея хранения адресов играет для ссылок особую роль, поскольку в ссылке хранятся данные о местонахождении другой величины. Скалярная величина, содержащая адрес области памяти, называется ссылкой. Значение, хранящееся в памяти по данному адресу, называется субъектом (referent) (рис. 11.1). Субъект может относиться к одному из встроенных типов данных (скалярная величина, массив, хэш, ссылка, код или глоб) или представлять собой пользовательский тип, основанный на одном из встроенных типов.

Reference      Ox83c6c Referent
ARRAY (Ox83c6c)     (3, 1 is a magic number')

Рис.11.1. Ссылка и субъект


Субъекты в Perl типизованы. Это означает, что ссылку на массив нельзя интерпретировать как ссылку на хэш. При подобных попытках инициируется исключение. В Perl не предусмотрен механизм преобразования типов, и это было сделано намеренно. На первый взгляд кажется, что ссылка - обычный адрес с сильной типизацией. На самом деле это нечто большее. Perl берет на себя автоматическое выделение и освобождение памяти (сборку мусора) для ссылок так же, как и для всего остального. С каждым блоком памяти в Perl связан счетчик ссылок, который определяет количество ссылок па данный субъект. Память, используемая субъектом, возвращается в пул свободной памяти процесса лишь при обнулении счетчика ссылок. Тем самым гарантируется, что вы никогда не получите недопустимую ссылку - забудьте об аварийных завершениях и ошибках защиты, часто возникающих при неправильной работе с указателями в С. Освобожденная память передается Perl для последующего использования, но лишь немногие операционные системы возвращают ее себе. Это связано с тем, что в большинстве схем распределения памяти используется стек, а при освобождении памяти в середине стека операционная система не сможет вернуть ее без перемещения всех остальных блоков. Перемещение нарушит целостность указателей и прикончит вашу программу. Чтобы перейти от ссылки к субъекту, снабдите ссылку символом типа для тех данных, к которым вы обращаетесь. Например, если $sref является ссылкой на скалярную величину, возможна следующая запись:
print $$sref; # Выводится скалярная величина, на которую ссылается     $sref $$sref =3; # Присваивается субъекту $sref


Для обращения к отдельному элементу массива или хэша, на который у вас имеется ссылка, используется ассоциативный оператор, оператор -> ("стрелка") - например, $rv->[37] или $rv->{"wilma"}. Помимо разыменования ссылок на массивы и хэши, стрелка также применяется при косвенном вызове функций через ссылки - например, $code_ref->("arg1", "arg2") (см. рецепт 11.4). Если вы работаете с объектами, то с помощью стрелки можно вызывать их методы, $object->methodname("arg1", "arg2"), как показано в главе 13 "Классы, объекты и связи". Правила синтаксиса Perl делают разыменование сложных выражений нетриви-1ьной задачей. Чередование правых и левых ассоциативных операторов не рекомендуется. Например, $$х[4] - то же самое, что и $х->[4]; иначе говоря, $х интерпретируется как ссылка на массив, после чего из массива извлекается четвертый элемент. То же самое записывается в виде ${$х}[4]. Если вы имели в виду "взять четвертый элемент @х и разыменовать его в скалярное выражение", воспользуйтесь ${$х[4]}. Старайтесь избегать смежных символов типов ($@%&) вез-.'-. кроме простых и однозначных ситуаций типа %hash = %$hashref. Приведенный выше пример с $$sref можно переписать в виде:
print ${$sref}; # Выводится скалярная величина, на которую ссылается
$sref ${$sref} =3; # Присваивается субъекту $sref
Некоторые программисты для уверенности используют только эту форму. Функция ref получает ссылку и возвращает строку с описанием субъекта. Стро ка обычно принимает одно из значений SCALAR, ARRAY, HASH или CODE, хотя иног-да встречаются и другие встроенные типы GLOB, REF, 10, Regexp и LVALUE. Если re-вызывается для аргумента, не являющегося ссылкой, функция возвращает false При вызове ref для объекта (ссылки, для субъекта которой вызывалась функция bless) возвращается класс, к которому был приписан объект: CGI, IO::Socket или даже
ACME::Widget. Ссылки в Perl можно создавать для субъектов уже определенных или определяемых с помощью конструкций [ ], { } или sub { }. Использовать оператор \ очень просто: поставьте его перед субъектом, для которого создается ссылка. Например, ссылка на содержимое массива @аrrау создается следующим образом:
$rv = \@array;


Создавать ссылки можно даже для констант; при попытке изменить значенш субъекта происходит ошибка времени выполнения:
$р1 = \3.14159;

$$pi =4; # Ошибка

Анонимные данные

Ссылки на существующие данные часто применяются для передачи аргументов функции, но в динамическом программировании они бывают неудобны. Иногда ситуация требует создания нового массива, хэша, скалярной величины или функции, но вам не хочется возиться с именами. Анонимные массивы и хэши в Perl могут создаваться явно. При этом выделяется память для нового массива или хэша и возвращается ссылка на нее:
$aref = [ 3, 4, 5 ]; # Новый анонимный массив
$href = { "how" => "Now", "Brown" => "Cow" }; # Новый анонимный хэш
В Perl также существует возможность косвенного создания анонимных субъектов. Если попытаться присвоить значение через неопределенную ссылку, Perl автоматически создаст субъект, который вы пытаетесь использовать.
undef $aref;
@$aref = (1, 2, 3);
print $aref;
ARRAY(Ox80c04fO)
Обратите внимание: от undef мы переходим к ссылке на массив, не выполняя фактического присваивания. Perl автоматически создает субъект неопределенной ссылки. Благодаря этому свойству программа может начинаться так:
$а[4][23][53][21] = "fred";
print $a[4][23][53][21];
fred print $a[4][23][53];
ARRAY(Ox81e2494) print $a[4][23];
ARRAY(Ox81e0748) print $a[4];
ARRAY(Ox822cd40)


В следующей таблице перечислены способы создания ссылок для именованных и анонимных скалярных величин, массивов, хэшей и функций. Анонимные тип-глобы выглядят слишком страшно и практически никогда не используются. Вместо них следует применять 10: :Handle->new().

Скалярная величина Массив Хэш Функция

\$scalar \@аггау \%hash \&function

\do{my $anon} { СПИСОК } { СПИСОК } sub { КОД }

Отличия именованных субъектов от анонимных поясняются на приведенных далее рисунках. На рис. 11.2 изображены именованные субъекты, а на рис. 11.3 - анонимные. Иначе говоря, в результате присваивания $а = \$Ь переменные $$а и $Ь занимают одну и ту же область памяти. Если вы напишете $$а = 3, значение $Ь станет равно 3.

Initial state:

$a=\$b;

$$а=3;

print "$$a $b\n 3 3

0х305108

 

 

Ox3051f00

 

 

5

0х305108

Ox3051f00

SCALAR Ox351f00

5

0х305108

Ox3051f00

SCALAR Ox351f00

5

;

 

 


Рис. 11.2. Именованные субъекты
Все ссылки по определению оцениваются как true, поэтому, если ваша функция возвращает ссылку, в случае ошибки можно вернуть undef и проверить возвращаемое значение следующим образом:
$op_cit = cite($ibid) or die "couldn't make a reference"

Initial state:

$$а=3;

print "$$a $b\n 3 3

0х305108

 

 

Ox3051f00

*--- (made by Perl)

 

 

0х305108

SCALAR Ox351f00

3

:

 

 


Рис. 11.3. Анонимные субъекты
Оператор undef может использоваться с любой переменной или функцией Perl для освобождения занимаемой ей памяти. Однако не следует полагать, что при вызове undef всегда освобождается память, вызываются деструкторы объектов и т. д. В действительности оператор всего лишь уменьшает счетчик ссылок на 1. Без аргумента undef дает неопределенное значение.

Записи

Ссылки традиционно применялись в Perl для обхода ограничения, согласно которому массивы и хэши могут содержать только скаляры. Ссылки являются скалярами, поэтому для создания массива массивов следует создать массив ссылок на массивы. Аналогично, хэши хэшей реализуются как хэши со ссылками на хэши: массивы хэшей - как массивы ссылок на хэши; хэши массивов - как хэши ссылок на массивы и т. д. Имея в своем распоряжении эти сложные структуры, можно воспользоваться ими для реализации записей. Запись представляет собой отдельную логическую единицу, состоящую из различных атрибутов. Например, запись, описывающая человека, может содержать имя, адрес и дату рождения. В С подобные вещи называются структурами (structs), а в Pascal - записями (RECORDs). В Perl для них не существует специального термина, поскольку эта концепция может быть реализована разными способами. Наиболее распространенный подход в Perl заключается в том, чтобы интерпретировать хэш как запись, где ключи хэша представляют собой имена полей записи, а ассоциированные величины - значения этих полей. Например, запись "человек" может выглядеть так:
$Nat = { "name" => "Leonhard Euler",
"Address" => "1729 Ramanujan Lane\nMathworld, PI 31416",
"Birthday" => Ox5bb5580,

Поскольку ссылка $NAT является скалярной величиной, ее можно сохранить с элементе хэша или массива с информацией о целой группе людей и далее использовать приемы сортировки, объединения хэшей, выбора случайных записей и т,д" рассмотренные в главах 4 и 5. Атрибуты записи, в том числе и "человека" из нашего примера, всегда являются скалярами. Конечно, вместо строк можно использовать числа, но это банально. Настоящие возможности открываются в том случае, если атрибуты записи также представляют собой ссылки. Например, атрибут "Birthday" может храниться в виде анонимного массива, состоящего из трех элементов: день, месяц и год. Выражение $person->{"BIrthday"}->[0] выделяет из даты рождения поле "день". Дата также может быть представлена в виде хэша, для доступа к полям которого применяются выражения вида $person->{ "Birthday" }->{"day"}. После включения ссылок в коллекцию приемов перед вами откроются многие нетривиальные и полезные стратегии программирования. На этом этапе мы концептуально выходим за пределы простых записей и переходим к созданию сложных структур, которые представляют запутанные отношения между хранящимися в них данных. Хотя они могут использоваться для реализации традиционных структур данных (например, связанных списков), рецепты второй части этой главы не связаны ни с какими конкретными структурами. В них описываются обобщенные приемы загрузки, печати, копирования и сохранения обобщенных структур данных. Завершающая программа этой главы демонстрирует работу с бинарными деревьями.

> Смотри также -------------------------------
perlref( 1); perllol( 1); perldsc( 1).


copyright 2000 Soft group