Глава 2. Конструкции языка Perl

Переменные, константы, выражения
Общие сведения о переменных
Переменные в Perl
Скалярные переменные
Скалярные константы и выражения
Массивы
Ассоциативные массивы
Управляющие конструкции
Блок-схемы
Условная конструкция
Циклы
Процедуры
Определение и объявление процедур
Параметры процедуры
Возврат из процедуры
Вызов процедуры
Локализация переменных
Рекурсия
Ссылки
Создание ссылок
Использование ссылок
Копирование ссылок
Определение вида ссылки

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

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

Для хранения значений переменные используют участки памяти (как правило, оперативной памяти). Таким образом, с переменной связывается адрес (номер самого первого байта переменной) и длина участка. Данные разных типов могут занимать участки разной длины. Поэтому во многих алгоритмических языках переменные различаются по типу данных, для хранения которых они предназначены. Такое явление называется типизацией. Другое предназначение типизации — определить перечень операций, которые допускаются над значениями. Например, для числовых данных разрешаются арифметические операции, для строковых — строковые операции, а для логических — логические.

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

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

Приведём примеры, где могут встретиться массивы и структуры.

Массив уместен для хранения квадратов первых десяти целых неотрицательных чисел. Тогда в ячейках (элементах) массива с индексами 0 , 1 , 2 , , 9 будут храниться числа 0 , 1 , 4 , , 81 . Во многих алгоритмических языках, в том числе в Perl, принята нумерация элементов массива, начиная с нуля, но в некоторых (Pascal) — с единицы.

Примером использования структур может служить цветовая схема RGB. В этой модели различные цвета представляются как смесь трёх так называемых основных цветов — красного (Red), зелёного (Green) и синего (Blue). Поэтому структура, состоящая из трёх компонент — количеств основных цветов в смеси, пригодна для хранения цветовых значений. Отдельные компоненты (поля) в структуре можно снабдить собственными именами, скажем, red, green, blue.

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

Таковы общие сведения о переменных в информатике. В последующих разделах будет рассказано, каковы особенности переменных в языке Perl.

В языке Perl имеются и простые, и сложные переменные. Простые переменные ещё называются скалярными. Сложные переменные в Perl бывают двух видов — массивы и ассоциативные массивы (хэши).

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

[Примечание]Примечание

В программу на Perl можно включить инструкцию, которая расширит понятие буквы, после чего станет возможным включать в имена переменных буквы национальных алфавитов — русские, греческие, грузинские, армянские — вплоть до китайских иероглифов. Мы не приветствуем такую возможность, поскольку она противоречит традициям программирования. Мы считаем, что использование неанглийских букв в именах — mauvais ton.

Заглавные и маленькие варианты буквы в именах считаются различными.

Переменная в зависимости от своего вида получает знак отличия — значок перед своим именем. Для скалярных переменных это знак доллара $ (Scalar [ˈskeılə] — $calar — скаляр). Для массивов это собака @ (Array [əˈreı] — @rray — массив). Наконец, для ассоциативных массивов это знак процента % (Hash [hæʃ] — %ash — хэш).

Значки перед именами позволяют создавать одноимённые переменные разных видов: так, скалярная переменная $a не имеет ничего общего с переменной-массивом @a. Иными словами, в Perl имеются раздельные пространства имён для скаляров, массивов и ассоциативных массивов.

[Примечание]Примечание

В Perl имеются переменные, чьи имена не соответствуют приведённому правилу, поскольку включают не разрешённые символы или начинаются с цифры, например, $^O, или $0, или $!. Это встроенные (специальные) переменные. Например, $^O содержит строку с названием операционной системы, в которой запущена программа (на моём компьютере это linux). Переменная $0 содержит имя файла, содержащего текст исполняющейся программы. В переменную $! записывается текст сообщения о последней случившейся системной ошибке (к примеру, после попытки открыть несуществующий файл в переменной будет содержаться строка Нет такого файла или каталога, если система настроена под русский язык).

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

Однако представьте, какой вселенский хаос наступит, если сложение назвать вычитанием, а синус переименовать в тангенс! И в программировании нужно давать переменным, процедурам, классам, файлам с программами такие имена, которые отражают назначение соответствующего объекта.

Часто случается так, что сущность переменной или процедуры получается выразить с использованием нескольких слов. Как составить имя из нескольких слов, если пробелы запрещены в именах? В разных языках программирования сложились определённые стили наименования объектов. В языке C наиболее распространён стиль, в котором отдельные слова в составе имени пишутся маленькими буквами и разделяются знаками подчёркивания. В языке Java знаки подчёркивания не вставляются, однако каждое слово, кроме первого, начинается с заглавной буквы. В языке Lisp, как и в C, слова в составе имени пишутся с маленькой буквы, но разделяются дефисом (дефис там разрешён в именах):

языкпримеры имён
Cline_count, draw_circle, open_file_for_reading
JavalineCount, drawCircle, openFileForReading
Lispline-count, draw-circle, open-file-for-reading

Мы предпочитаем стиль, принятый в Java (он называется camelCase) и будем последовательно использовать именно его в этой книге.

Скалярные переменные служат для хранения скалярных значений — чисел, строк, ссылок (о ссылках речь пойдёт в разделе «Ссылки»).

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

Perl
$a=54;

Знак равенства не должен нас обмануть: в языке Perl он означает не то, к чему мы привыкли в математике. Это обозначение для присваивания. В результате присваивания значение переменной, чьё имя указано слева от знака присваивания, становится равным значению выражения справа от знака.

В данном примере выражение справа очень простое — это число (числовая константа). Но выражение справа может и не быть константой. Там могло быть, к примеру, арифметическое выражение:

$a=50+4;

или

$a=27*2;

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

Здесь следует подчеркнуть отличие равенства в математике и присваивания в информатике. Отношение равенства симметрично (утверждение x = y равносильно утверждению y = x ). В программировании это не так. Будет ужасной ошибкой написать в программе

54=$a;

или

2*27=$a;

Слева от знака присваивания может стоять только переменная — нечто, чему можно присваивать значение.

Кроме того, равенство x = x + 1 в математике никогда не выполняется. В случае присваивания, напротив, можно написать

$i=$i+1;

Между прочим, не только можно, но и зачастую нужно. Мы видим, что имя переменной здесь используется и справа от знака присваивания, в арифметическом выражении. Это вторая возможность использования переменной — для взятия хранящегося в ней значения. Смысл последнего присваивания — увеличение значения переменной $i на единицу.

Если переменная, которой присваивается значение, не существовала, она молча создаётся. Если переменная уже использовалась, хранящееся в ней значение заменяется на новое.

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

($x, $y, $z)=(3, 4, 5);

Такое списочное присваивание равносильно трём обычным:

$x=3;
$y=4;
$z=5;

Иногда требуется обменять значения двух переменных. Пусть, к примеру, значения переменных $x и $y равны 2 и 3 соответственно. Наивное решение не сработает:

$x=$y;	теперь значение $x равно 3
$y=$x;	а теперь и $y равно 3

Тут не обойтись без вспомогательной переменной $tmp, в которую предварительно запишем старое значение $x, а потом отправим в переменную $y:

$tmp=$x;
$x=$y;
$y=$tmp;

(имя $tmp происходит от английского temporary — временный; так часто называют переменные, которые нужны для однократного использования, как в нашем случае).

Списочное присваивание избавляет нас от необходимости прибегать к вспомогательной переменной:

($x, $y)=($y, $x);

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

Константы в Perl бывают двух типов — числовые и строковые.

В Perl имеется несколько способов записи числовых констант:

Текст будет позже.

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

Нужно понимать, что с точки зрения программы совершенно неважно, какое именно изображение у того или иного символа. Программа, работающая со строками, оперирует не изображениями символов, а их номерами. Поскольку все известные человечеству символы можно перенумеровать очень многими способами, есть проблема: одну и ту же строку (последовательность номеров символов) можно интерпретировать по-разному в зависимости от выбранного способа (от выбранной кодовой таблицы). Среди всевозможных способов перенумеровать символы имеется несколько стандартных, получивших широкое распространение. Однако отложим этот вопрос до лучших времён (см. раздел «Кодовые страницы»), а сейчас примем, что выбрана конкретная кодовая таблица, устанавливающая соответствие между символами и их номерами. Таким образом, строка представляет из себя что-то вроде массива целых неотрицательных чисел из ограниченного диапазона, но, в отличие от «настоящих» массивов языка Perl, о которых речь пойдёт в разделе «Массивы», размещается в памяти более компактно.

К примеру, при выбранной кодовой таблице UCS строка Hello, world! состоит из символов с номерами 72 101 108 108 111 44 32 119 111 114 108 100 33 , а строка Школа №54 — из символов с номерами 1064 1082 1086 1083 1072 32 8470 53 52 . Обратите внимание, что английская буква o и русская буква о имеют различные номера в кодовой таблице (111 и 1086), несмотря на совершенно одинаковое изображение.

Чтобы поместить в программу текстовую строку, имеются несколько способов. Во-первых, можно заключить текст в одиночные кавычки ' (правильное название для этой кавычки — апостроф). Вот так: 'текст'. Если требуется включить в текст сам знак апострофа, нужно сделать так, чтобы он не воспринимался как правый ограничитель строки. Для этого помещаем перед апострофом знак обратной косой черты (бэкслэш): 'Rock\'n\'Roll'. Это способ поместить в программу строку Rock'n'Roll. Если вдруг зачем-то понадобится создать строку со знаком бэкслэш, за которым следует знак апострофа, поступаем так: '…\\\'…' — перед апострофом ставим бэкслэш, а перед предшествующим ему знаком бэкслэш помещаем ещё один. Такое применение бэкслэша имеется во многих компьютерных языках: этот символ лишает следующий за ним символ какого-то особенного смысла, или, наоборот, придаёт ему особый смысл. В результате бэкслэш сам приобретает особый смысл, а вот предшествующий ему бэкслэш уже лишает его особого смысла.

Заключение строки в апострофы позволяет вводить в текст программы строковые константы (то есть неизменяемые, постоянные строки).

Другой способ даёт возможность создавать строки, в которых есть изменяемая часть. Это строки в двойных кавычках ". Их использование почти такое же, что и использование апострофов, но есть отличия. Главное отличие состоит в том, что внутри таких строк происходит интерполяция переменных, то есть подстановка вместо имён переменных их значения.

Поясним это явление на примере. Фрагмент программы

$user='cerebellum';
print "Hello, $user!";

напечатает строку Hello, cerebellum!, поскольку Perl, обнаружив между двойными кавычками имя скалярной переменной, подставит вместо имени значение. Впрочем, можно вполне обойтись и без двойных кавычек:

$user='cerebellum';
print 'Hello, '.$user;

Здесь мы соединяем (сцепляем) вместе две строки, константу 'Hello, ' и строку в переменной $user, и печатаем командой print то, что получилось. Правда, так получается длинней, особенно если требуется поместить в строку несколько переменных, да и от кавычек и операторов конкатенации (.) рябит в глазах.

Следующий пример иллюстрирует одну возможную неприятность во время интерполяции переменных:

$hours=3;
$minutes=14;
print "Total time: $hoursh $minutesm";

Вместо ожидаемой строки Total time: 3h 14m программа выведет

Первые четыре предупреждения намекают нам, что переменные $hours, $minutes, $hoursh, $minutesm использованы только по одному разу, то есть без всякой пользы. Это, конечно, не криминал, но, как правило, свидетельствует об ошибке программирования (возможно, опечатке). Позвольте, а что это за переменные $hoursh, $minutesm? Это результат близкого соседства имени переменной и единицы измерения времени внутри двойных кавычек. Perl видит "…$hoursh…" и пытается подставить вместо законного имени переменной $hoursh её значение. Но эта переменная упоминается здесь первый и последний раз, она ни разу не получала значение. Последние две жалобы как раз об этом. Откуда грамматическому анализатору знать, что имя подставляемой переменной, по нашему замыслу, $hours, а следующая буква h — это просто буква? Для борьбы с двусмысленностью поступаем так:

print "Total time: ${hours}h ${minutes}m";

Если после имён подставляемых переменных идёт символ, который запрещено использовать в именах, эта проблема не возникает.

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

print "Invest only $1 and become millionaire!";

Получаем следующее:

Дело в том, что здесь выполняется нежелательная подстановка переменной $1 (да, это специальная переменная, и её имя не соответствует нашим канонам; она имеет отношение к регулярным выражениям, см. главу 30. «Регулярные выражения»). В данном случае были бы уместней апострофы:

Perl
print 'Invest only $1 and become millionaire!';

А можно «защитить» знак доллара, поставив перед ним наш любимый бэкслэш:

Perl
print "Invest only \$1 and become millionaire!";

Кстати, бэкслэш внутри строк в двойных кавычках открывает нам миллион новых возможностей. Сочетания этого знака со многими другими (не только \\ и \', как между апострофами) имеют особый смысл. Приведём таблицу, где перечисляются многие такие сочетания:

комбинациязначение
\\бэкслэш \
\nсимвол конца строки
\tсимвол табуляции
\fсимвол конца страницы
\eсимвол «Escape»
\aсимвол «Bell», при печати на экран раздаётся гудок
\x{2230}символ с заданным (в фигурных скобках) шестнадцатеричным номером, в данном случае
\N{VOLUME INTEGRAL}символ с заданным (в фигурных скобках) названием символа, в данном случае снова

Комбинации \x{…} и \N{…} бывают полезны, если символ неудобно или невозможно ввести в текст программы непосредственно, например, из-за отсутствия его на клавиатуре. Шестнадцатеричные номера и названия символов берутся из таблицы UCS.

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

комбинациязначение
\lделает следующий символ строчным (если это буква)
\uделает следующий символ прописным (опять же если это буква)
\Lделает все последующие буквы строчными вплоть до ближайшего \E или же до конца строки
\Uделает все последующие буквы прописными вплоть до ближайшего \E или до конца строки
\Eобозначает конец действия команд \L и \U

Спрашивается, а зачем могут понадобится эти команды? Почему бы не писать просто "Moscow" вместо "\umoscow"? Конечно же полезными эти возможности становятся при использовании интерполяции переменных в ""-строках. Рассмотрим пример, в котором переменные $surname и $firstname содержат фамилию и имя, записанные целиком прописными буквами, а требуется вывести их в обычном формате (первая буква прописная, остальные — строчные):

Perl
$surname='MARX'; $firstname='KARL'; print "\u\L$firstname\E \u\L$surname\E\n";

Karl Marx

В Perl имеется конструкция, позаимствованная из командного языка операционной системы Linux. Это так называемые here-строки. Если в текст программы нужно поместить многострочный текст, удобно воспользоваться такой конструкцией:

$text=<<__TEXT__;
Придешь усталый,
                вешаться хочется.
Ни щи не радуют,
                ни чая клокотанье.
А чайкой поплещешься —
                      и мёртвый расхохочется
от этого
        плещущего щекотания.
__TEXT__

Поясним, что здесь происходит. Переменной $text присваивается строковое значение, которое занимает несколько строк. Вместо строки в правой части присваивания помещаются символы <<, за которыми следует маркер конца текста (в данном случае это __TEXT__). Само строковое значение считывается из последующих строк программы, пока не будет обнаружена строка программы, содержащая тот же самый маркер. Сам маркер при этом не становится частью строкового значения.

Приведённый пример служит альтернативой следующего кода:

$text="Придешь усталый,\n                вешаться хочется.\nНи щи не радуют,\n                ни чая клокотанье.\nА чайкой поплещешься —\n                      и мёртвый расхохочется\nот этого\n        плещущего щекотания.\n";

Впрочем, если вам больше по душе последняя запись, пользуйтесь на здоровье.

Для маркера можно выбрать любое имя, какое позволяется для имён переменных. Мы предпочитаем записывать маркеры заглавными буквами, обрамляя двойными знаками подчёркивания для лучшего зрительного выделения.

Внутри <<__MARKER__-строк действуют те же правила, что и внутри ""-строк. Там точно так же производится интерполяция переменных и действуют комбинации, начинающиеся со знака бэкслэш:

print <<__EMAIL__
Здравствуйте, $name!

Вы были зарегистрированы на сайте swindler-shop.com под логином:
Логин: $login
Пароль: $password

Вы можете зайти на сайт с этим логином и отслеживать состояние своих заказов.

С уважением, компания «Swindler Shop».
__EMAIL__

Этот пример показывает, что here-строки возможно использовать не только в правой части присваивания.

Если требуется, чтобы внутри here-строк не производилась интерполяция переменных и обработка бэкслэш-комбинаций, заключим маркер в одиночные кавычки сразу после <<:

Массивы в Perl — это упорядоченные списки скалярных значений (элементов). Значения в списке пронумерованы начиная с нуля. Номера (индексы) элементов, таким образом, принимают значения от 0 до n 1 , где n — количество элементов в массиве.

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

@a=(1, 2, 3, 4, 5, "математика", "механика");

Как показывает этот листинг, элементами массива могут быть как числа, так и строки.

Ещё один способ создать массив — присвоить ему другое значение-массив:

@b=@a;

В результате такого присваивания получится массив @b — точная копия массива @a. Последующие изменения в массиве @b никак не отразятся на содержимом массива @a, и наоборот.

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

$c[2]=54;
$c[5]="школа";

Если массив @c не существовал, он будет создан, в нём будет шесть элементов. Все они, за исключением третьего и шестого, окажутся неопределёнными, а третий и шестой получат соответственно числовое и строковое значение. Иными словами, при присваивании значения ячейке, отсутствующей в массиве, он расширяется до нужного размера.

Обратите внимание на знак $ в выражениях $c[2] и $c[5]. Хотя перед именем массива @c используется знак @, здесь используется доллар в знак того, что мы имеем дело с отдельной ячейкой массива, содержащей скалярное значение.

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

@numbers=0..99;

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

@alphabet='A'..'Z';

В отличие от многих других языков программирования, в Perl допустимо обращаться не только к отдельной ячейке массива, но и к целому набору ячеек. Например, скопировать из массива @a в массив @b ячейки с индексами 7, 3, 5 (именно в таком порядке) можно следующим образом:

@b=@a[7, 3, 5];

Мы не ошиблись. На этот раз перед именем массива @a, несмотря на последующие квадратные скобки, ставится именно @, а не $, поскольку выражение @a[7, 3, 5] даёт список скалярных значений.

Выражения вида @a[…], где в квадратных скобках указан список индексов, можно использовать и в левой части операции присваивания. Эта интересная возможность позволяет переставлять местами некоторые элементы массива без использования вспомогательных переменных. К примеру, поменять местами первый и последний элементы в массиве можно классическим способом

$tmp=$a[0];
$a[0]=$a[-1];
$a[-1]=$tmp;

Но можно сделать то же самое короче и яснее так:

@a[0, -1]=@a[-1, 0];

Команда shift (shift [ʃıft] — сдвинуть) извлекает из массива самый первый элемент. При этом элемент из массива удаляется, а на его место сдвигаются его соседи справа. Команда unshift делает противоположное — добавляет в массив скалярное значение слева; при этом все элементы массива сдвигаются вправо. Аналогичные манипуляции с массивом осуществляют команды pop (pop [pɔp] — вытолкнуть) и push (push [puʃ] — втолкнуть), но только с правого конца массива:

@days=('пн', 'вт', 'ср', 'чт', 'пт', 'сб', 'вс');
$monday=shift @days;		теперь @days содержит
				('вт', 'ср', 'чт', 'пт', 'сб', 'вс'),
				а $monday содержит 'пн'

push @days, $monday;		теперь в @days содержится
				('вт', 'ср', 'чт', 'пт', 'сб', 'вс', 'пн')

unshift @days, (pop @days);	вернём всё в первоначальное состояние

Команды unshift и push могут добавлять в массив (соответственно слева и справа) и по нескольку ячеек сразу.

Реже используемая команда splice обобщает те возможности, которые предоставляются командами shift, pop, unshift, push. Она, во-первых, вырезает из массива кусок, начинающийся с заданного индекса и имеющий заданную длину, возвращая его. Во-вторых, при желании, на место вырезанной части вставляется новый список элементов.

Чтобы прояснить поведение всех этих пяти команд, приведём таблицу эквивалентов:

$b=shift @a;
($b, @a)=@a;
($b)=splice @a, 0, 1;

$b=pop @a;
($b, @a)=@a[-1..$#a-1];
($b)=splice @a, -1;

unshift @a, @b;
@a=(@b, @a);
splice @a, 0, 0, @b;

push @a, @b;
@a=(@a, @b);
splice @a, @a, 0, @b;

Удалить все элементы массива проще всего, присвоив ему пустой массив:

@a=();

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

В обычных массивах не может быть двух элементов с одинаковыми индексами. В ассоциативных массивах точно так же невозможны одинаковые ключи у двух элементов. Таким образом, ассоциативный массив представляет собой коллекцию пар «ключ — значение», в которой один и тот же ключ не может встречаться дважды.

Ассоциативные массивы могут быть созданы присваиванием списка пар «ключ — значение» — массива, в котором чередуются ключи и значения:

%h=(
		'кошка',	'котёнок',
		'собака',	'щенок',
		'лошадь',	'жеребёнок',
		'овца',		'ягнёнок',
		'свинья',	'поросёнок',
		'корова',	'телёнок',
		'коза',		'козлёнок',
	);

Элементы массива в правой части присваивания, имеющие чётные индексы, станут ключами в ассоциативном массиве, а следующие за ними элементы с нечётными индексами — соответствующими этим ключам значениями.

Мы в вышеприведённом примере для наглядности разбили список в правой части присваивания на пары, разместив их на отдельных строках, хотя могли бы всё присваивание записать в одну строку или как-нибудь иначе. Для большей выразительности мы могли бы соединить вместе ключи и значения оператором =>, оставив запятые как разделители пар:

%h=(
		'кошка'=>	'котёнок',
		'собака'=>	'щенок',
		'лошадь'=>	'жеребёнок',
		'овца'=>	'ягнёнок',
		'свинья'=>	'поросёнок',
		'корова'=>	'телёнок',
		'коза'=>	'козлёнок',
	);

Конечно же остаётся возможность использовать оператор qw/…/, если ни ключи, ни значения не содержат пробелов:

%h=qw/
		кошка	котёнок
		собака	щенок
		лошадь	жеребёнок
		овца	ягнёнок
		свинья	поросёнок
		корова	телёнок
		коза	козлёнок
	/;

Читателя, возможно, интересует: что будет, если ассоциативному массиву присваивается список нечётной длины? Ничего страшного не произойдёт — просто значение, соответствующее последнему ключу, останется неопределённым. А что если среди ключей (элементов с чётными индексами в массиве справа) встретятся одинаковые? И это не беда: каждый раз, когда повторно встречается какой-нибудь ключ, старое соответствующее ему значение заменяется новым.

В обычном массиве кроме самих значений хранится также их порядок (все значения пронумерованы их индексами). Это обстоятельство позволяет мгновенно найти значение по индексу: место в памяти, где хранится нужное значение, легко определить с помощью простых арифметических операций, зная индекс и адрес начала массива. В ассоциативном массиве пары «ключ — значение» принципиально неупорядочены: они размещаются в памяти таким образом, чтобы поиск значения по ключу происходил бы как можно быстрее. Имеется возможность перебрать пары в ассоциативном массиве, однако не стоит ожидать, что они будут появляться при переборе в каком-нибудь определённом порядке (скажем, в алфавитном порядке ключей, или значений, или в порядке, в котором пары добавлялись в ассоциативный массив). Алгоритм, размещающий в памяти пары, может меняться от одной версии Perl к другой.

Ассоциативные массивы можно присваивать:

%i=%h;

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

Отдельные элементы ассоциативного массива можно использовать в выражениях, в том числе для присваивания им значений:

$h{'утка'}='утёнок';
print $h{'утка'};

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

$eng2dut{salt}='zout';
$eng2dut{sugar}='suiker';
$eng2dut{matches}='lucifers';

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

Если добавить значение в несуществующий ассоциативный массив, он тихо создаётся. Здесь ситуация такая же, как и обычными массивами.

Команда keys позволяет получить список ключей из ассоциативного массива:

%digits=(zero=>0, one=>1, two=>2, three=>3);
@names=keys %digits;

После выполнения этого кода массив @names будет содержать строки 'zero', 'one', 'two', 'three' в неизвестном порядке. Как мы уже упоминали, порядок, в котором хранятся пары ключ — значение в ассоциативном массиве, не определён, и полагаться на какой-то определённый порядок будет ошибкой. Если нужно работать с ключами или со значениями в ассоциативном массиве по порядку, потребуется сортировка, обсуждение которой мы отложили до главы 18. «Сортировка».

Аналогичная команда values выдаёт список значений. После выполнения

Perl
@numbers=values %digits;

в массиве @numbers окажутся числа 0, 1, 2 и 3, опять же в непредсказуемом порядке.

Команда delete используется для удаления отдельного ключа или списка ключей. Вместе с ключом удаляется и связанное с ним значение. После выполнения

Perl
delete $digits{two};

в ассоциативном массиве останутся лишь пары (zero=>0, one=>1, three=>3), а затем, после выполнения

Perl
delete @digits{qw/zero three/};

останется лишь пара (one=>1).

Можно опустошить ассоциативный массив %h вычурной командой

Perl
delete @h{keys %h}

Но короче и быстрее будет присвоить ассоциативному массиву пустой список:

Perl
%h=();

Информатика-54© А. Н. Швец