Ассоциативные массивы в Perl являются реализацией хэш-таблиц. Ассоциативные массивы, безусловно, являются уникальным и чрезвычайно полезным свойством языка Perl. Они чрезвычайно удобны для связи числовых или текстовых значений с текстовыми строками или при подсчете количества вхождений текстовых строк (при статистическом анализе).
В то время как обыкновенная переменная в Perl имеет префикс $, ассоциативные массивы имеют префикс %. Индексами (ключами) у ассоциативных массивов являются строки. Числовые ключи преобразуются в строки. Ключи заключаются в фигурные скобки, а не в квадратные, как это принято в обычных массивах. Значениями могут быть любые скалярные данные: строки, числа или ссылки.
Ассоциативный массив может быть декларирован напрямую как список пар ключ-значение:
%ages = ("Andy", 22, "Mike", 23, "Dr. Jones", 27, "Mr. Smyth", 21, "Katrin", "who knows");
Обращение к элементам ассоциативного массива осуществляется следующим образом:
$ages{"Dave"} = 30;Здесь "Dave" является ключом, а 30 - значением. Обратите внимание, что обращение к значению осуществляется с префиксом $, т.к. оно является скалярным.
В отличие от обыкновенных массивов, у ассоциативных массивов порядок следования элементов не определен и к ним нельзя обратиться по числовому индексу.
Можно перебрать по очереди все элементы ассоциативного массива с помощью функции foreach:
foreach $person (keys %ages) { print "I know the age of $person is $ages{$person}\n"; } foreach $age (values %ages) { print "Somebody is $age\n"; }
При вызове keys возвращается список ключей ассоциативного массива. При вызове values возвращается список значений ассоциативного массива. Эти функции возвращают списки в одинаковом порядке, хотя он и не имеет ничего общего с порядком добавления элементов в массив.
В скалярном контексте функции keys и values возвращают количество пар ключ-значение, хранящихся в ассоциативном массиве:
$num_keys = scalar keys %ages;Данный вызов также сбрасывает итератор, связанный с ассоциативным массивом. Это может пригодиться при преждевременном выходе из цикла с помощью функции last.
Существует также функция each, возвращающая двухэлементный список, состоящий из ключа и значения. При каждом новом вызове она возвращает новую пару:
while (($person, $age) = each(%ages)) { print "$person is $age\n"; }Данная функция не позволяет сортировать массив. Нельзя добавлять новые пары в массив в процессе итерирования.
Существует возможность нахождения ключа по его значению. Для этого необходимо создать обратный ассоциативный массив:
%by_value = reverse %by_key; $key = $by_value{$value};Но данный способ не совсем эффективен. Более эффективным (но не более быстрым) является следующий способ:
while (($key, $value) = each %by_key) { $by_value{$value} = $key; }
Ассоциативный массив может быть преобразован в списковый простым его присвоением переменной спискового массива. Обратное преобразование также достигается простым присвоением:
@info = %ages; # @info является списковым массивом. # Он теперь имеет 10 элементов $info[5]; # Возвращает значение 27 из массива @info %moreages = @info; # %moreages является ассоциативным массивом. # Он теперь идентичен %ages
Внутренний способ представления ассоциативных массивов не позволяет предсказать порядок выборки пар ключ-значение. Чтобы упорядочить выборку, необходимо отсортировать список ключей или значений:
@persons = sort keys %ages; # сортировка по ключу @persons = sort { $ages{$a} cmp $ages{$b} } keys %ages; # сортировка по значению
В следующем примере мы попытаемся отсортировать по числовому значению в обратном порядке, если значения окажутся одинаковыми, то отсортируем по длине ключа, а если и они совпадут, произведем сортировку последовательным ASCII сравнением ключей:
@persons = sort { $ages{$b} <=> $ages{$a} || length($a) <=> length($b) || $a cmp $b } keys %ages;
При наличии более сложного критерия сортировки сравнение целесообразно вынести в отдельную функцию:
foreach $key (sort ByAge keys(%ages)) { } sub ByAge { $ages{$a}<=>$ages{$b}; }
Другой особенностью ассоциативных массивов является возможность существования неопределенного значения в паре ключ-значение.
Наличие ключа в ассоциативном массиве можно проверить функцией exists:
exists $ages{'Andy'} возвращает true exists $ages{'Nick'} возвращает false
Наличие определенного (действительного) значения можно проверить функцией defined:
defined $ages{'Andy'} возвращает trueМожно удалить из массива значение, не удаляя ключа функцией undef:
undef $ages{'Andy'}Теперь:
$ages{'Andy'} возвращает false defined $ages{'Andy'} возвращает false exists $ages{'Andy'} возвращает true
Можно также удалить из массива целиком всю пару ключ-значение функцией delete:
delete $ages{'Andy'}В этом случае:
defined $ages{'Andy'} возвращает false exists $ages{'Andy'} возвращает false
Восполним этот пробел.
$VisitCounter{$person}{$date}=$value;
Перебор элементов трехмерного ассоциативного массива с их сортировкой осуществляется следующим образом:
foreach $person (sort keys(%VisitCounter)) { $DateHash=\$VisitCounter{$person}; foreach $date (sort keys(%{$$DateHash})) { $value = $VisitCounter{$person}{$date}; } }