Приглашаем посетить
Сумароков (sumarokov.lit-info.ru)

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

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

Ассоциативные массивы в 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};
   }
}