Глава3. Массивы, хеши и записи таблицы символов

Коротко

В этой главе будет предпринята попытка выяснить, как организованы данные в таких крайне важных структурах, как массивы и хеши. Кроме того будет описана работа с другим важным типом данных — записями таблицы символов (typeglob).

Массивы

Массивы представляют собой состоящие из скаляров списки с целочисленным индексом. Индекс позволяет ссылаться на скаляры, занесенные в массив — это очень полезно для программирования, так как позволяет увеличивать или уменьшать индекс и получать доступ из программы к любому элементу, работая сразу со всем массивом. Для создания массива необходимо присвоить  переменной-массиву в качестве значения список (в Perl такие переменные начинаются с префикса @):

@array = (1, 2, 3);

Чтобы ссылаться на отдельные элементы массива следует указать индекс элемента в квадратных скобках и заменить префикс @ на префикс $ — это показывает, что мы работаем со скаляром. Обратите также внимание, что индексы для массивов Perl отсчитываются от нуля:

print $array[0];

1

В этой главе основное внимание будет уделяться стандартным одномерным массивам. В главе 13, речь в которой идет о структурах данных, рассказывается также о двумерных массивах.

Хеши

Хеш-таблицы (хешированные таблицы, или просто хеши), называемые также ассоциативными массивами для доступа к отдельным элементам данных используют не индексы, а ключи. При использовании хешей значения ассоциируются с текстовыми ключами, например,

$hash{fruit} = apple;

$hash{sandwich} = hamburger;

$hash{drink} = bubbly;

Теперь можно использовать эти данные, применив для доступа к ним ключ:

print $hash{sandwich};

hamburger

Представление ваших данных в виде хешей, как правило более интуитивно (в отличие от массивов), поскольку для извлечения данных используются ключи. Хеши являются идеальным средством для создания записей данных.

Записи таблицы символов typeglob

Тип данных typeglob — еще один встроенный тип Perl. Разыменовывающий префикс для него — звездочка * . Тип данных typeglob используется для создания синонимов для типов данных, ассоциированных с указанным именем. (Соответственно, звездочка, которая является также и универсальным шаблоном при выборе файлов, отражает дух. нового типа данных.) Например, при наличии переменных $data и @data:

$data = "Here’s the data";

@data = (1, 2, 3);

с помощью конструкции typeglob можно ссылаться на эти переменные под другим именем:

*alsodata = *data;

Теперь $alsodata является синонимом для $data, а @alsodata — для @data:

print "$alsodata\n";

Here’s the data

Подсказка: Тип данных typeglob  *имя, — это внутренняя структура (запись таблицы символов), хранящая информацию обо всех переменных с именем имя, (то есть, о скалярной переменной $имя, массиве @имя, хешее %имя, подпрограмме &имя, дескрипторе файла >имя). В частности, для каждого типа данных в таблицу символов записывается адрес области памяти (ссылка), где оно хранится. Присвоив одной записи содержимое другой записи, мы просто заставили ссылки двух таблиц ссылаться на одну и ту же область памяти.

На этом с введением покончено. В следующем разделе эти типы данных рассматриваются более подробно.

Непосредственные решения

Создание массивов

Имена массивов начинаются с символа @. Чтобы создать массив, следует присвоить переменной массива в качестве аргумента список:

@array = (1, 2, 3);

Чтобы увидеть результат, выведем новый массив:

@array = (1, 2, 3);

print @array;

123

Подсказка: Команда print интерпретирует массив как список и объединяет его элементы в единое целое. Более красивые способы вывода содержимого массива вы найдете в разделе «Вывод массивов».

На отдельные элементы массива можно ссылаться, указывая индекс в квадратных скобках и заменяя префикс имени символом $ (потому что элемент массива — скаляр):

@array = (1, 2, 3);

print $array[0];

1

Кроме чисел, в массиве можно хранить и другие скаляры, например, строки:

@array = ("one", "two", "three");

print @array;

onetwothree

Поскольку Perl при обработке списков пропускает все пробельные символы (включая символы новой строки), аргумент в виде списка значений можно легко разбить на несколько строчек:

@array = (

      "one", "two", "three",

      "four", "five", "six",);

print @array;

onetwothreefourfivesix

Можно также использовать оператор повторения, что в данном случае создаст массив из ста нулей:

@array = (0) x 100;

Наконец, можно использовать операторы-кавычки (см. раздел «Обработка кавычек и слов без кавычек» и таблицу 2.3 в предыдущей главе):

@array = qw(one two three);

print @array;

onetwothree

Кроме описанных способов, для создания массивов и добавления в них новых элементов, используются также функции push и unshift (они рассматриваются ниже в этой главе).

Хотя элементы в массивах Perl по умолчанию отсчитываются с нуля, можно изменить это правило, занеся новое базовое значение в специальную переменную $[. Ее использование, однако, считается дурным тоном. Хоть этот способ и не запрещен, вряд ли его кто-то одобрит. (В этом отличие Perl от языков типа Java, в которых методы, вызывающие неодобрение, просто запрещаются.)

Использование массивов

После создания массива можно ссылаться на его отдельные элементы, как на скаляры, указывая индекс в квадратных скобках и используя префикс $ перед именем массива:

@array = ("one", "two", "three");

print $array[1];

two

Поскольку для доступа к элементам массива можно использовать индекс, массивы могут работать как таблицы подстановок. Так в следующем примере, десятичное число из диапазона от 0 до 15, вводимое пользователем, заменяется на его шестнадцатеричный эквивалент:

@array = (’0’, ’1’, ’2’, ’3’, ’4’, ’5’, ’6’, ’7’,

          ’8’, ’9’, ’A’, ’B’, ’C’, ’D’, ’E’, ’F’);

while (<>) {

   $hex = $array[$_];

   print "$hex\n";

}

С точки зрения программиста тот факт, что массивы обеспечивают доступ к элементам данных по номеру, является мощным средством, позволяющим последовательно обрабатывать массив, изменяя в цикле индекс. Например, так выводятся все элементы массива по очереди:

@array = ("one", "two", "three");

for ($index = 0; $index <= $#array; $index++) {

   print $array[$index], " ";

one two three

Обратите внимание на использование конструкции $#array. В Perl она означает последний индекс массива @array (см. также далее раздел «Определение длины массива»).

Операции push и pop

Кроме присвоения списком, для изменения массивов можно использовать функции push и pop. Функция push добавляет один или несколько элементов в конец массива:

push массив, список

Значения списка добавляются в конец массива, а длина массива увеличивается на длину списка.

Функция pop удаляет данные из массива:

pop массив

pop

Она удаляет и возвращает в качестве своего результата последнее значение в массиве, сокращая длину массива на один элемент. Следующий пример показывает, как последовательно добавляются элементы в (изначально пустой) массив:

push (@array, "one");

push (@array, "two");

push (@array, "three");

print $#array, ’:’, @array;

2:onetwothree

(обратите внимание, что $#array на единицу меньше числа элементов массива). Аналогичным образом удаляются данные из конца массива:

@array = ("one", "two", "three");

$variable1 = pop (@array);

print "$variable1/$#array";

three/1

Операции shift и unshift

Функции shift и unshift работают с началом массива так же, как push и pop с его концом. Вот как используется shift:

shift массив

shift

Эта функция удаляет первый элемент массива, возвращая его как результат. Массив сокращается на один элемент, а остальные элементы сдвигаются на одну позицию вправо.

А вот как выглядит функция unshift:

unshift массив, список

Эта функция выполняет операцию, противоположную действиям функции shift. Она последовательно добавляет список в начало массива, сдвигая остальные элементы вправо и увеличивая длину массива на размер списка.

Пример использования функции shift для извлечения элемента массива:

@array = ("one", "two", "three");

$variable1 = shift (@array);

print "$variable1/$#array";

one/1

Определение длины массива

Если определен массив с именем @array, переменная $#array содержит индекс последнего его элемента. Имя может быть любым: если массив назван, например, @phonenumbers, то индекс его последнего элемента содержится в переменной $#phonenumbers.

Например, при наличии массива

@array = (1, 2, 3);

чтобы вывести число его элементов, надо добавить единицу к переменной $#array:

@array = (1, 2, 3);

print "\@array has " . "($#array + 1) . " elements.";

@array has 3 elements.

(Единица прибавляется потому, что индексы массива отсчитываются с нуля).

Использование массива в скалярном контексте также возвращает его длину. Чтобы перевести массив в скалярный контекст, можно выполнить над ним фиктивную числовую операцию (например, прибавить ноль):

@array +0

либо, что более профессионально, использовать функцию scalar:

@array = (1, 2, 3);

print "\@array has " . scalar (@array) . " elements.";

@array has 3 elements.

либо, наконец, попросту присвоить массив скалярной переменной:

@array = (1, 2, 3);

$variable = @array;

print "\@array has $variable elements.";

@array has 3 elements.

Подсказка: Обратите внимание, что если вы присвоите скалярной переменной список, а не массив, то результатом будет не длина списка, а последний элемент списка. В этом, как и во многих других примерах, проявляется контекстная зависимость команд Perl, о которой мы уже говорили в разделе 2.

Увеличение или сужение массива

Чтобы изменить число элементов в массиве, достаточно присвоить новое значение переменной $#array, хранящей индекс последнего элемента. Вот пример такой операции:

@array = (1, 2, 3);

$#array = 10;

$array[5] = "Here is a new element! ";

print $array[5];

Here is a new element!

(все «нововведенные» элементы массива получают неопределенное значение undef).

Можно полностью очистить массив, присвоив индексу последнего элемента отрицательное число:

$#array = -1;

Слияние двух массивов

Чтобы объединить два массива, их можно присвоить третьему массиву как список. В следующем примере массивы @array1 и @array2 объединяются в массив @bigarray:

@array1 = (1, 2, 3);

@array2 = (4, 5, 6);

@bigarray = (@array1, @array2);

С новым массивом можно выполнять любые операции, допустимые для массивов:

print $bigarray[5];

6

Получение среза массива

Срез массива — это часть массива, создаваемая с помощью оператора диапазона. Он имеет формат [x..y] и соответствует массиву с элементами, имеющими индексы x, x+1, ..., и далее до y включительно.

В следующем примере с помощью этой конструкции будет создан подмассив @array2, состоящий из элементов 2 и 3 массива @array:

@array = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

@array2 = @array[2..3];

print join(", ", @array2);

3, 4

Циклы и массивы

Как уже говорилось раньше в этой главе, с помощью цикла for можно последовательно перебрать элементы массива, явно обращаясь к каждому элементу по его индексу:

@array = ("one", "two", "three");

for ($loop_ind=0; $loop_ind<=$#array; $loop_ind++) {

   print $array[$loop_ind];

}

onetwothree

Можно также воспользоваться другим оператором цикла — foreach. Он непосредственно перебирает элементы массива:

@array = (1, 2, 3, 4, 5);

foreach $element (@array) {

   print "$element\n";

}

1

2

3

4

5

Можно организовать цикл сразу по нескольким массивам, перечислив последние в списке (что нтерполирует элементы массивов в один список)

@array1 = (1, 2, 3);

@array2 = (4, 5, 6);

foreach $element (@array1, @array2) {

   print "$element\n";

}

1

2

3

4

5

6

Вместо цикла foreach можно использовать цикл for (на самом-то деле for и foreach — это один и тот же оператор):

@array = (1, 2, 3, 4, 5);

for $element (@array) {

   print "$element\n";

}

1

2

3

4

5

При желании можно даже использовать цикл for без ссылки на конкретную переменную цикла, используя специальную переменную по умолчанию $_:

@array = (1, 2, 3, 4, 5);

for (@array) {

   print;

}

12345

Итак, цикл может организовываться множеством способов. Конкретный вариант зависит только от вашего вкуса и от результатов, которые должна дать программа.

Вывод массива

Если требуется вывести массив, можно передать его функции print как

@array = ("one", "two", "three");

print "Here is the array: ",@array, ".\n";

Here is the array: onetwothree.

Недостаток этого метода в том, что print рассматривает массив, как список, а потому печатает элементы один за другим, что порождает на выходе слово "onetwothree".

Более удачная идея — использовать интерполирование строки, состоящей из заключенных в двойные кавычки слов. Указав в теле строки имя массива, получим:

@array = ("one", "two", "three");

print "Here is the array: @array.\n";

Here is the array: one two three.

Perl интерполирует массив, используя по умолчанию литеру-разделитель полей (хранится в специальной переменной $,). Возможно, требуется разбить элементы массива не пробелами, а запятыми. Для этого надо присвоить символ «запятая» переменной $,, и вот что получается в результате:

@array = ("one", "two", "three");

$, = ",";

print "Here is the array: @array.\n";

Here is the array: one,two,three.

Еще лучше использовать функцию join, создав из массива строку и явно разделив элементы запятыми:

@array = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

print join(", ", @array);

1, 2, 3, 4, 5, 6, 7, 8, 9, 10

Естественно, можно использовать цикл for или foreach по всем элементам массива:

@array = ("one", "two", "three");

foreach $element (@array) {

   print "Current element = $element\n";

}

Current element = one

Current element = two

Current element = three

Ну и в завершение надо заметить, что, поскольку для доступа к любому элементу массива достаточно указать его индекс, можно организовать вывод массива любым способом и в любом формате.

Сращивание (splicing) массива

Сращивание массива означает добавление новых элементов из списка, возможно, с замещением элементов, уже хранящихся в массиве. Для этой цели используется функция splice:

splice массив, смещение, длина, список

splice массив, смещение, длина

splice массив, смещение

Если в качестве параметра задан список, то splice удаляет из массива элементы, описываемые параметрами смещение и длина, и замещает их элементами списка.

В списковом контексте функция splice возвращает элементы, удаленные из массива. В скалярном контексте она возвращает последний удаленный элемент (или undef, если элементы не удалялись). При отсутствии параметра длина, splice удаляет все элементы до конца массива, начиная с элемента с индексом смещение.

Приведем несколько примеров. Сначала в массив, в котором уже имеются элементы "one" и "two", будет добавлен элемент "three":

@array = ("one", "two");

splice (@array, 2, 0, "three");

print join(", ", @array);

one, two, three

Теперь в конец старого массива будет добавлен второй массив:

@array1 = ("one", "two");

@array2 = ("three", "four");

splice (@array1, 2, 0, @array2);

print join(", ", @array1);

one, two, three, four

Наконец, во время сращивания можно удалять элементы одного из массивов. Например, элемент "zero" первого массива заменяется массивом, содержащим элементы "two", "three" и "four":

@array = ("one", "zero");

@array2 = ("two", "three", "four");

splice (@array, 1, 1, @array2);

print join(", ", @array);

one, two, three, four

Инвертирование массива

Чтобы инвертировать массив (расположить его элементы в обратном порядке), используется функция reverse:

@New = reverse @array;

Сортировка массива

Для сортировки массива применяется функция sort (для массивов она вызывается и работает точно так же, как и для списков — см. описание sort в предыдущей главе):

@new = sort {$a <=> $b} @array;

С ее помощью можно выполнять самые хитроумные сортировки — например, вот так можно сортировать массив по убыванию:

@new = sort {$b <=> $a} @array;

Создание хешей

Хеши (хеш-таблицы, хешированные таблицы) в более ранних версиях Perl назывались  ассоциативными массивами, и это описывает их суть более содержательно, так как для доступа к данным вместо числового индекса используется ключ (то есть текстовая строка), которая ассоциируется со значением.

Поскольку в хешах ссылки на данные осуществляются с помощью ключей, а не чисел, доступ к элементам обычно интуитвнее проще, чем в случае массивов. Однако организация циклов для хешей может оказаться труднее, потому что к элементу хеша невозможно получить доступ с помощью числового итератора цикла.

Имена хешей начинаются с префикса %. Так создается пустая хеш-таблица:

%hash = ();

Как и в случае массивов, при работе с элементами хешей надо использовать разыменовывающий префикс $. Вот, например, как поместить в хеш новые элементы:

%hash = ();

$hash{fruit} = apple;

$hash{sandwich} = hamburger;

$hash{drink} = bubbly;

(Здесь fruit — первый ключ хеш-таблицы, и он соответствует значению apple; sandwich является вторым ключом со значением hamburger, и так далее.)

Обратите внимание, что для ключа использются фигурные ({}), а не квадратные ([]) скобки, как это было с индексами массивов.

Отдельные элементы извлекаются из хеша также с помощью ключей:

%hash = ();

$hash{fruit} = apple;

$hash{sandwich} = hamburger;

$hash{drink} = bubbly;

print $hash{sandwich};

hamburger

На самом деле нет необходимости создавать пустой хеш, чтобы начать его заполнение. Встретив обращение к несуществующему хешу, Perl создает его автоматически. (Это — шаг навстречу программистам, попытка заставить систему работать так, как вы ожидаете.) Поэтому следующий код работает не хуже предыдущего:

$hash{fruit} = apple;

$hash{sandwich} = hamburger;

$hash{drink} = bubbly;

print $hash{sandwich};

hamburger

Не забывайте, что Perl при считывании новых элементов опускает «пробельные символы». Поэтому, например, массивы  с множеством элементов можно сделать более прозрачными для понимания, использовав многострочные конструкции:

@array = (

      "one", "two", "three",

      "four", "five", "six"

);

Точно так же можно создавать хеш-таблицы, указывая пары ключ/значение:

%hash = (

   fruit    , apple,

   sandwich , hamburger,

   drink    , bubby

);

print "$hash{fruit}\n";

apple

Вместо запятой, разделяющей пары, можно использовать синоним — конструкцию =>. С этим оператором отношение ключ/значение выглядит прозрачнее, так что программисты часто записывают команду создания хеша так:

%hash = (

   fruit    => apple,

   sandwich => hamburger,

   drink    => bubby

);

print "$hash{fruit}\n";

apple

Оператор => не выполняет специальных операций — это в точности то же самое, что и обычная запятая, за исключением того факта, что он вынуждает интерпретировать слово, стоящее слева, как строку. Например, команда

print "x"=>"y"=>"z";

xyz

делает то же, что и команда

print "x", "y", "z";

xyz

Не запрещается использовать ключи, содержащие пробелы. Например, можно создать элемент хеша с ключом ice cream:

$hash2{cake} = chocjlade;

$hash2{pie} = blueberry;

$hash2{’ice cream’} = pecan;

и когда потребуется использовать его:

print "$hash{’ice cream’}\n";

pecan

При создании ключей можно использовать интерполирование текстовых строк, ограниченных двойными кавычками, а также просто переменные.

$value = $hash{$key};

Хеши обеспечивают мощное средство для хранения данных, но ссылаться на элементы хеш-таблицы напрямую, с помощью числового индекса, нельзя. Это, конечно же, не значит, что нельзя организовать цикл по элементам хеша, — об этом рассказывается далее в этой главе в разделе «Циклы по элементам хеш-таблицы».

Использование хешей

После того, как вы создали хеш, можно работать с ним, обращаясь к его элементам с помощью ключей:

$value = $hash{$key};

Кроме этого, с помощью оператора присвоения легко добавить в хеш новые элементы, как это было сделано в примере из предыдущего раздела:

$hash{fruit} = apple;

$hash{sandwich} = hamburger;

$hash{drink} = bubbly;

print $hash{sandwich};

hamburger

В списковом контексте хеш интерполируется: пары ключ/значение переходят в единый список элементов. В скалярном контексте подставляется значение истина (true), если в хеше есть хотя бы одна пара ключ/значение, и ложь (false), если хеш пуст.

Добавление элементов в хеш

Чтобы добавить новый элемент (то есть пару ключ/значение) в хеш, используйте оператор присвоения. В следующем примере в хеш %hash добавляются два значения:

%hash = ();

$hash{$key} = $value;

$hash{$key2} = $value2;

Хеши можно создавать, используя присвоение списка, и точно также можно добавлять новые элементы. В следующем примере таким образом к хешу %hash добавляется новая пара ключ/значение:

%hash = (

   fruit    => apple,

   sandwich => hamburger,

   drink    => bubby

);

%hash = (%hash, dressing, ’blue cheese’);

print $hash{dressing};

blue cheese

Этот пример работает, потому что оператор списка () сперва интерполирует хеш %hash в список, а затем последний расширяется еще на одну пару ключ/значение. Из-за интерполяции, которая происходит до присвоения списком, в данном случае нельзя использовать сокращенную форму с оператором +=:

%hash += (dressing, ’blue cheese’);      # Это не работает

Проверка хеша на наличие элемента

Чтобы проверить, есть ли в хеше некоторый элемент, можно использовать функцию exists. Например, в следующем примере в хеше %hash ищется элемент с ключом vegetable:

$hash{fruit} = apple;

$hash{sandwich} = hamburger;

$hash{drink} = bubbly;

if (exists($hash{"vegetable"})) {

   print "Element exists.";

} else {

   print "Element does not exist.";

}

Element does not exist

Удаление элементов хеша

Чтобы удалить элемент из хеша, используется функция delete. Вот пример, в котором удаляется элемент хеша и затем проверяется, существует ли еще такой элемент:

$hash{fruit} = apple;

$hash{sandwich} = hamburger;

$hash{drink} = bubbly;

delete ($hash{"fruit"});

if (exists($hash{"fruit"})) {

   print "Element exists.";

} else {

   print "Element does not exist.";

}

Element does not exist

Циклы по хешу

Существует множество способов органицаии цикла по элементам хеша. Функция each позволяет использовать записи хеша (то есть и ключ, и значение) целиком.

Рассмотрим пример. Сначала создадим хеш-таблицу:

$hash{fruit} = apple;

$hash{sandwich} = hamburger;

$hash{drink} = bubbly;

Теперь можно извлечь из нее пары ключ/значение, используя функцию each и присвоение списком:

$hash{fruit} = apple;

$hash{sandwich} = hamburger;

$hash{drink} = bubbly;

while (($key, $value) = each(%hash)) {

   print"$key => $value\n";

};

drink => bubbly

sandwich => hamburger

fruit => apple

Использование each в данном случае полезно, так как для каждого элемента хеша при одном ее вызове возвращается как ключ, так и значение.

Кстати, элементы хеша извлекаются не в том порядке, в каком они заносились. Дело в том, что Perl хранит их, используя собственный внутренний алгоритм, оптимизирующий затраты памяти и обеспечивающий легкость доступа. Сортировка элементов описывается в разделе «Сортировка хеша» далее в этой главе.

При работе с элементами хеша можно использовать цикл foreach. Например, совместно с функцией keys, возвращающей список ключей хеша, он позволяет последовательно перебрать весь хеш:

$hash{fruit} = apple;

$hash{sandwich} = hamburger;

$hash{drink} = bubbly;

foreach $key (keys %hash){

   print $hash{$key} . "\n";

}

bubbly

hamburger

apple

Однако вместо keys можно сразу воспользоваться функцией values. Она возвращает список значений, хранящихся в хеше. С ней цикл, выводящий все значения хеша, принимает следующий вид:

$hash{fruit} = apple;

$hash{sandwich} = hamburger;

$hash{drink} = bubbly;

foreach $value (values %hash){

   print "$value\n";

}

bubbly

hamburger

apple

Как видите, несмотря на то, что к элементам хеша нельзя получить доступ с помощью числового индекса, Perl предоставляет достаточно средств для их перебора.

Вывод хеша

Чтобы вывести содержимое хеша можно интерполировать его в строку, заключенную в двойные кавычки:

$hash{fruit} = apple;

$hash{sandwich} = hamburger;

$hash{drink} = bubbly;

print "@{[%hash]}\n";

drink bubbly sandwich hamburger fruit apple

В результате такой печати хеш переведен в списковый контекст, то есть представлен в виде пар ключ/значение, расположенных друг за другом. Функция eachв данной ситуации дает более удачное решение:

$hash{fruit} = apple;

$hash{sandwich} = hamburger;

$hash{drink} = bubbly;

while (($key, $value) = each(%hash)) {

   print "$key: $value\n";

};

drink: bubbly

sandwich: hamburger

fruit: apple

Есть множество других способов организовать цикл по элементам хеша (см. раздел «Циклы по элементам хеша» ранее в этой главе).

Сортировка хеша

Для сортировки хешей используется та же функция sort, что и для сортировки списков и массивов. Вот, например, как хеш сортируется по значению ключа:

$hash{fruit} = apple;

$hash{sandwich} = hamburger;

$hash{drink} = bubbly;

foreach $key (sort keys %hash) {

   print "$key => $hash{$key}\n";

}

drink => bubbly

fruit => apple

sandwich => hamburger

Точно так же вместо сортировки по ключу можно выполнить сортировку по значению:

$hash{fruit} = apple;

$hash{sandwich} = hamburger;

$hash{drink} = bubbly;

foreach $value (sort values %hash) {

   print "$value\n";

}

apple

bubbly

hamburger

Слияние хешей

Для объединения двух хешей можно использовать присвоение списком. Например, допустим, есть два хеша:

$hash1{fruit} = apple;

$hash1{sandwich} = hamburger;

$hash1{drink} = bubbly;

 

$hash2{cake} = chocolate;

$hash2{pie} = blueberry;

$hash2{’ice cream’} = pecan;

Оба хеша могут быть объединены следующим образом:

%bighash = (%hash1, %hash2);

print $bighash{’ice cream’};

pecan

Использование хешей и массивов в присвоении списком

При присвоении списком могут использоваться хеш-таблицы и массивы. Несколько хешей или списков справа от оператора присваивания интерполируются в один список, так что не возникает никаких синтаксических проблем. Рассмотрим пример из предыдущего раздела:

$hash1{fruit} = apple;

$hash1{sandwich} = hamburger;

$hash1{drink} = bubbly;

$hash2{cake} = chocolate;

$hash2{pie} = blueberry;

$hash2{’ice cream’} = pecan;

При присвоении списком хеши объединяются в один список:

%bighash = (%hash1, %hash2);

print $bighash{’ice cream’};

pecan

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

Вот как происходит присвоение списку, состоящему из двух скалярных переменных и одного массива (массив обязательно должен стоять в списке переменных последним):

($var1, $var2, @array) = (1, 2, 3, 4, 5, 6, 7, 8);

print "$var1\n";

print "$var2\n";

print "@array\n";

1

2

3 4 5 6 7 8

Использование типа данных typeglobтип-глобов

Конструкции typeglob играют в Perl роль синонимов обычных переменных. (В ранних версиях они выступали в качестве аналога ссылок, но теперь в Perl появились полноценные ссылки.) Этот тип данных позволяет связать имя одной переменной (например, data) с именем другой (например, alsodata). В результате имена типа $alsodata, @alsodata и %alsodata будут ссылаться на те же объекты, что и $data, @data и %data (то есть $alsodata будет ссылаться на те же данные, что и $data, @alsodata — на те же данные, что и @data, и так далее).

Приведем пример. Сначала создаются переменные $data и @data:

$data = "Here is the data.";

@data = (1, 2, 3);

Теперь имени data присваивается синоним alsodata:

$data = "Here is the data.";

@data = (1, 2, 3);

*alsodata = *data;

А теперь вместо имени data можно использовать имя alsodata:

$data = "Here is the data.";

@data = (1, 2, 3);

*alsodata = *data;

print "$alsodata\n";

print @alsodata;

Here is the data.

123

На самом деле происходит вот что: запись таблицы символов, обозначаемая как *имя, хранит информацию обо всех переменных с общим именем имя, —например, запись *data текущей таблицы символов хранит информацию о переменных $data, @data, %data, и так далее. В частности, для каждого типа данных записывается адрес области памяти, где это данное хранится, или ссылка на нее (то есть, ссылка на скалярную переменную $data, ссылка на массив @data, ссылка на хеш-таблицу %data, и т. д.).

При присвоении конструкций typeglobтип-глобов Perl копирует записи таблицы символов для одного имени в другое имя. Символ *, используемый в конструкции typeglob, можно считать шаблоном любого префикса типа данных ($, %, и так далее). Более подробно процесс копирования записей таблицы символов будет рассмотрен в следующем разделе.

Подсказка: Чтобы после операции *alsodata = *data переменная $alsodata стала синонимом $data, на момент присвоения переменная $data может и не быть определена. Поскольку Perl компилирует код непосредственно перед выполнением сценария, память для $data будет выделена заранее и ссылка на нее уже попадет в структуру *data. Иначе вместо ссылки на переменную $data в запись таблицы символов *alsodata была бы скопирована информация о том, что такой скалярной переменной еще нет, и переменные $data и $alsodata не могли бы ссылаться на одно и то же значение.

При использовании конструкций typeglob необязательно копировать всю запись таблицы символов, относящуюся к данному имени. Если справа от оператора присваивания стоит ссылка только на один тип данных (например, скаляр), то и новое имя и выступать синонимом только для указанного типа:

$data = "Here is the data.";

@data = (1, 2, 3);

*alsodata=\$data;   # Введем синоним только для скаляров

В данном случае $alsodata становится синонимом $data, но @alsodata не будет синонимом для @data, %alsodata — для %data, и т. д. Иными словами, такая команда сработает нормально:

print "$alsodata\n";

Here is the data.

а такая работать не будет:

print @alsodata;

Тип данных typeglob и записи в таблице символов

Как уже говорилось, Perl хранит информацию о переменных во внутренней таблице имен (таблице символов) в виде отдельных записей. Каждая запись таблицы символов представляет собой тип typeglob. Этот тип данных можно считать небольшим хешем, значениями которого являются ссылки на область памяти, где хранятся соответствующие переменным данные.

Ключами для таких хешей (хотя этой информации вы, возможно, не найдете в документации Perl) выступают имена типов данных, записанные заглавными буквами (то есть ARRAY, HASH, и так далее). Это может быть полезным, если требуется непосредственный доступ к таблице символов Perl. Допустим, в программе определена переменная со значением 5:

$variable = 5;

В этом случае *variable — имя переменной типа typeglob, хранящей информацию о $variable, а *variable{SCALAR} — ссылка на значение переменной $variable. Чтобы получить значение переменной $variable через ссылку на область данных в таблице символов, использется разыменовывающий оператор $:

$variable = 5;

print ${*variable{SCALAR}};

5