Информационные технологииStfw.Ru 🔍

Ассоциативные массивы. Переменные.

Глава 3
🕛 01.11.2006, 16:11
  • 3.4. Ассоциативные массивы.
  • 3.5. Переменные.
  • Вопросы для самоконтроля.
  • Упражнения.

      3.4. Ассоциативные массивы

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

    Ассоциативные массивы отличаются от массивов скаляров тем, что в них для ссылки на элементы используются строки, а не числовые индексы, т. е. концептуально они представляют собой список не просто значений элементов массива, а последовательность ассоциированных пар ключ/значение. Ключ является строковым литералом, и именно он и используется для доступа к ассоциированному с ним значению массива.

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

    (ключ_1, значение_1, ключ_2, значение_2, ... , ключ_п, значение_п)

    Для хранения ассоциативных массивов, как и для других элементов данных, используются переменные, первым символом которых является символ процента "%". Переменные, в которых хранятся ассоциативные массивы, часто называют хеш-переменными. Ассоциативный массив создается во время операции присвоения такой переменной списка значений:

    %т = ("Имя", "Ларри", "Фамилия", "Уолл");

    Замечание

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

    В ассоциативном массиве %т ключами являются строки "имя" и "Фамилия", а ассоциированными с ними значениями, соответственно, "ларри" и "УОЛЛ". Теперь, чтобы получить значение, соответствующее какому-либо ключу, следует воспользоваться конструкцией:

    $т{"ключ"}

    Обратите внимание, что при работе с элементом ассоциативного массива, символ хеш-переменной "%" заменяется на символ скалярной переменной "$". Аналогично мы поступали и при ссылке на элемент массива скаляров. Единственное отличие - ключ задается в фигурных скобках. Итак, чтобы, например, присвоить некоторой скалярной переменной значение элемента хеш-массива %т, следует воспользоваться следующим оператором:

    $surname = $m{"Фамилия"};

    Теперь скалярная переменная $ surname имеет в качестве своего значения строку Уолл.

    Замечание

    Интерпретация списка как последовательности пар ключ/значение происходит только при операции его присвоения хеш-переменной. Если список, присваивается переменной массива скаляров, то он интерпретируется как простая последовательность значений элементов массива.

    Инициализация хеш-массива с помощью списка, элементы которого отделяются друг от друга символом запятая ",", не очень удобно, так как в длинном списке трудно выделять соответствующие пары ключ/значение. Для улучшения наглядности пару ключ/значение можно соединить последовательностью символов "=>", заменив ей разделяющую запятую в списке. По правде говоря, и запятая ",", и символы "=>" представляют собой знаки операций в Perl, причем операция "=>" эквивалентна операции "запятая" с той лишь разницей, что ее левый операнд всегда интерпретируется как строковый литерал, даже если он не заключен в кавычки.

    Замечание

    Интерпретация левого операнда операции "=>" как строкового литерала справедлива для последовательности символов, в которой используются буквы латинского алфавита. Буквы русского алфавита вызовут ошибку интерпретации, так как последовательность символов не будет распознана как слово языка Perl.

    Рассмотренный нами ранее хеш-массив %т можно инициировать и таким способом:

    %т = (

    "Имя" => "Ларри",

    "Фамилия" => "Уолл" );

    Если бы мы хотели в качестве индексов имени и фамилии использовать английские слова name и surname, то этот же ассоциированный массив можно было бы задать следующим оператором:

    %т = (

    Name => "Ларри",

    Surname => "Уолл" );

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

    $т{"Имя"} = "Гарри"; # Изменили значение существующего элемента. $т{"Телефон"} = "345-56-78"; # Добавили новый элемент.

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

    Удалить элемент ассоциативного массива можно только с помощью встроенной функции delete:

    delete($m{"Телефон"}); # Удалили элемент с ключом "Телефон".

    Совет

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

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

    print keys(%m), "\n"; # Печать ключей. print values(%m), "\n"; # Печать значений.

    отобразит на экране монитора строку ключей и строку значений массива %т

    ФамилияИмяТелефон УоллЛарри345-56-11

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

    Замечание

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

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

    #! peri -w %friend = (

    "0001", "Александр Иванов",

    "0002", "Александр Петров",

    "0003", "Александр Сидоров" ); %city = (

    "0001", "Санкт-Петербург",

    "0002", "Рязань", '"0003", "Кострома" ) ; %job = ( -

    "0001", "учитель",

    "0002", "программист",

    "0003", "управляющий"

    ) ; . ' $person = "0001";

    print "Мой знакомый $friend{$person}\n"; print "живет в городе $city{$person}\n"; print "и имеет профессию $job{$person}.\п";

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

    Мой знакомый Александр Иванов живет в городе Санкт Петербург и имеет профессию учитель.

    Если изменить значение переменной $person на другой ключ, то отобразится связанная информация о другом человеке.

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

    Связанный список - это простейшая динамическая структура данных, расположенных в определенном порядке. Каждый элемент связанного списка состоит из некоторого значения, ассоциированного с данным элементом, и ссылки на следующий элемент списка. Последний элемент списка не имеет ссылки на следующий, что обычно реализуется в виде пустой ссылки. Для окончательного задания связанного списка следует объявить переменную, указывающую на первый элемент списка, которую называют заголовком. Для связанного списка определяются операции выбора, удаления и добавления элемента списка относительно заданного. Графически связанный список можно представить так, как показано на рис. 3.1, где указатель на следующий элемент обозначен серым цветом.


    Рис 3.1. Графическое представление связанного списка

    С помощью хеш-массивов связанный список реализуется просто. Для этого следует значение элемента задать в качестве ключа для следующего за ним элемента списка, определив таким образом указатель на следующий элемент. Значением последнего элемента в хеш-массиве (с ключом, равным значению последнего элемента связанного списка) будет пустая строка "". Переменная-заголовок должна иметь значение, равное ключу первого элемента списка. В примере 3.8 показана реализация связанного списка, а также добавление нового элемента.

    %linked_list = ( "начало" => "первый", "первый" => "третий",

    "третий" => ""

    );

    $header = "начало";

    # Добавление элемента со значением "второй"

    # после элемента со значением "первый".

    $temp = $linked_lis.t{ "первый"}; # Запомнили старый, указатель ., $linked_list{"второй"} = $temp; # Добавили новый элемент. $linked_list{"первый"} = "второй"; # Указатель на новый элемент.

    $item = $header;

    # Печать нового связанного.списка.

    while ($linked_list{$item}) { # Пока не дойдем до пустой строки ""

    print $linked_list{$item}, "\n"; # будем печатать значения элементов.

    $item = $linked_list{$item};

    }

    Результатом выполнения профаммы примера 3.8 будет печать значений элементов нового связанного списка в следующем порядке:

    первый второй третий

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

      3.5. Переменные

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

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

    Первый символ имени переменной языка Perl определяет ее тип. В языке Perl можно определить переменные трех типов: скаляр, массив и хеш-массив. Для каждого типа переменных интерпретатор создает собственное пространство имен, храня в нем идентификаторы объявленных в программе переменных заданного типа. Это позволяет создавать переменные разных типов с одинаковыми идентификаторами. В программе Perl бесконфликтно могут сосуществовать и скалярная переменная $var, и массив скаляров @var, и хеш-массив %var.

    Так как имя переменной всегда начинается с одного из символов "$", "@" или "%", то использование в качестве идентификатора "предопределенных" слов не приводит к конфликтам в процессе интерпретации программы.

    Следует отметить, что в языке Perl существует достаточно большой набор специальных встроенных переменных, которые можно использовать для получения или изменения разнообразной системной информации. Большинство из них являются скалярными переменными с "идентификатором", состоящим из одного специального символа, например, $~, $_ и т. д.

    (Подробно специальные переменные рассматриваются в главе 14.)

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

    Замечание

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

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

    В списковом контексте конструктор массива будет иметь значение, представляющее собой все значения списка в заданном порядке, тогда как в скалярном контексте он будет иметь значение, равное значению последнего элемента списка:

    @аггау = (0, 2, 4); # Массив скаляров баггау

    # содержит три элемента: 0, 2, 4. $last = (0, 2, 4); # Значение скалярной переменной $last равно 4.

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

    @new_array = Sarray; # Массив @new_array

    # содержит все элементы массива @аггау. $number = Sarray; # Скалярная переменная $number

    # равна 3 - числу элементов массива Sarray.

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

    ($а, $Ь) = (1, 2, 3); # $а = 1, $Ь = 2.

    ($а, $Ь, $с) = (1, 2); tt $а= 1, $Ь =2, $с = "".

    В языке Perl каждая операция имеет вычисляемое значение. Значением операции присваивания со скаляром в качестве правого операнда является значение этого скаляра в любом контексте. Если правым операндом является конструктор массива или массив, то в списковом контексте значением операции присваивания будет список элементов массива, а в скалярном контексте - число элементов массива.

    $х = ( @а = (1, 2) ); # $х = 2.

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

    %hash = ( I => I, 2 =>2, 3 => 3, 4=> 4 );

    @list = %hash; # @list = (blue, 3, green, 2, red, 1, black, 4)

    В скалярном контексте вычисляемым значением хеш-массива является строка, состоящая из числа использованных участков записей (bucket) и числа выделенных участков записей, разделенных символом "/". Если присвоить скалярной переменной хеш-массив %hash предыдущего примера, то ее значением будет строка "4/8", которая говорит, что 4 из 8 выделенных участков записей хеш-таблицы используется.

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

    @т = (1,2);

    defined @т; '# Возвращает 1, массив скаляров @т определен.

    defined $т; # Возвращает "", переменная $т не определена.

    Определенную переменную можно в любой момент сделать неопределенной, выполнив функцию undef с параметром, равным имени этой переменной:

    @т = (1,2);

    defined @т; # Возвращает 1, массив скаляров @т определен.

    undef @m;

    defined @m; tt Возвращает "", массив скаляров @т не определен.

    Если переменная сделана не определенной с помощью функции undef, TO она, естественно, теряет присвоенное ранее ей значение.

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

    (Более подробно локальные переменные описаны в главе 11.)

      Вопросы для самоконтроля

    1. Перечислите три встроенных типа данных языка Perl.

    2. В чем отличие числового литерала от строкового.

    3. Объясните различие между строкой, ограниченной одинарными кавычками, и строкой, ограниченной двойными кавычками.

    4. Каким образом можно выполнить системную команду из программы Perl?

    5. Что такое массив скаляров и ассоциативный массив?

    6. Как задаются в программе массивы и хеш-массивы?

    7. Как объявляются в программе переменные для хранения скалярных данных, массивов скаляров и хеш-массивов?

    8. Что такое интерполяция переменной?

    9. Можно ли интерполировать массивы скаляров и хеш-массивы?

    10. Какие два контекста для операции присваивания вы знаете, и как ведут себя массивы скаляров и хеш-массивы в них?

      Упражнения

    1. Найдите ошибки в следующем фрагменте кода Perl:

    $m.= 'Исходные данные:\п'; @data = ( 1, 2, 3, 4} ; print $m, 'Запись: Sdata';

    2. Что напечатают следующие операторы и почему:

    $т = "Скаляр \$т\п";

    @т = ( 1, 2, 3);

    print "Значение равно $т[0]\п";

    print "Значение равно $т [0]";

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

    4. Дополните программу примера 3.8 удалением первого и последнего элемента связанного списка. (Указание: воспользуйтесь функцией delete ().)

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

  • Учебник по Perl   Теги:

    Читать IT-новости в Telegram
    Информационные технологии
    Мы в соцсетях ✉