Глава 5 Хэши

Хэши

Введение

Как люди, так и части компьютерных программ взаимодействуют между собой самым причудливым образом. Отдельные скалярные переменные похожи на отшельников, ведущих замкнутое существование в рамках собственной личности. Массив напоминает партию, где множество К сожалению, отношения хэшей являются не равными, а подчиненными - например, "Энди - начальник Ната"; "Кровяное давление пациента - 112/62" или "Название журнала с индексом ISSN 1087-903X - The Perl Journal". Хэш всего лишь предоставляет удобные средства "Кто является начальником Ната?" или "Как называется журнал 1087-903X"? Вы не сможете спросить "Чьим начальником является Эндн?" Впрочем, поиску ответов на подобные вопросы посвящен один из рецептов этой главы. Однако у хэшей есть свои преимущества. В Perl хэш является встроенным типом данных. Благодаря применению хэшей многие сложные алгоритмы сводятся к простой выборке значений. Кроме того, хэши предоставляют быстрые и удобные средства для построения индексов Префикс % относится лишь к ссылкам на хэш в целом. Значение ключа представляет собой скалярную величину, поэтому для него используется символ $ (по аналогии с тем, как для ссылок на отдельный элемент массива используется префикс $). Следовательно, отношение "начальник Ната" должно записываться в виде $boss{"Nat"}. В обычных массивах используются числовые индексы, но индексы хэшей всегда являются строковыми. Ассоциированные значения могут быть произвольными скалярными величинами, в том числе ссылками. Используя ссылки в качестве ассоциированных значений, можно созда Хэши могут инициализироваться с помощью списков, содержащих пары "ключ/ значение":

%аgе = ( "Nat", 24, "Jules", 25, "Josh", 17 );


Такая запись эквивалентна следующей:

$age{"Nat"} = 24;
$age{"Jules"} = 25;
$age{"Josh"} = 17;

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

%food_color = (
"Apple" => "red", "Banana" => "yellow", "Lemon" => "yellow", "Carrot" => "orange" );


(хэш %i ooa_coior используется во многих примерах этой главы). Такая инициализация также является примером списковой эквивалентности - в некоторых отношениях хэш ведет себя так, словно он является списком пар "ключ/значение". Мы воспользуемся этим В отличие от обычной занятой, оператор => обладает особым свойством: любое предшествующее ему слово интерпретируется как строковое значение. Это позволяет убрать кавычки и сделать программу более попятной. Однословные ключи хэшей также автоматически интер

%food_color = (
Apple => "red", Banana => "yellow", Lemon => "yellow", Carrot => "orange"
Одно из важных свойств хэшей заключается в том, что их элементы хранятся в особой последовательности, обеспечивающей выборку. Следовательно, независимо от порядка занесения данных в хэш, порядок их хранения будет непредсказуемым.

> Смотри также --------------------------------
Описание функний unshift и splice в perlfunc(1).

5.1. Занесение элемента в хэш

Проблема

Требуется добавить в хэш новый элемент.

Решение

Присвоите нужное значение в записи вида:
$ХЭШ{$КЛЮЧ} = $ЗНАЧЕНИЕ;

Комментарий

Пропесс занесения данных в хэш весьма тривиален. В языках, где хэш не относится к встроенным типам данных, приходится беспокоиться о переполнении, изменении размеров и коллизиях в хэш-таблицах. В Perl обычное присваивание решает сразу все проблемы. Если к
# Хэш %food_color определяется во введении
$food_color{Raspberry} = "pink":
print "Known foods:\n";
foreach $food (keys %food_color) { print "$food\n";
}


Known foods:
Banana
Apple
Raspberry
Carrot
Lemon

Если в качестве ключа хэша используется неопределенная величина undef, она преобразуется в пустую строку "" (что сопровождается предупреждением при запуске с параметром -w). Вероятно, неопределенный ключ undef - это не то, что вы хотели. С другой Во внутренних алгоритмах хэширования Perl перестановки строки попадают на одну и ту же позицию. Если в ключах хэша многократно встречаются перестановки одной строки (скажем, "spare" и "craps"), быстродействие хэша заметно падает. На практике это происходи > Смотри также ------------------------------- Раздел "List Value Constructors" peiidata(1) рецепт 5.2.

5.2. Проверка наличия ключа в хэше

Проблема

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

Решение

Воспользуйтесь функцией exists:

# Содержит ли %ХЭШ ключ $КЛЮЧ?
if (ех1з1з($ХЭШ{$КЛЮЧ})) {
# Ключ существует
} else {
# Ключ не существует
}

Комментарий

В следующем фрагменте функция exists проверяет, присутствует ли ключ в хэше %food_color:

# Хэш %food_color определяется во введении
foreach $name ("Banana", "Martini") {
if (exists $food_color{$name}) {
print "$name is a tood.\n";
} else {
print "$name is a drink.\n";
}
}
Banana is a food. Martini is a drink, Функция exists проверяет только наличие ключа в хэше. Она не сообщает об ассоциированном значении, определено ли оно, истинно или ложно. На первый взгляд кажется, что отличия несущественны. Однако в действительности проблемы такого рода плодятся быстро, к

%аgе = ();
$age{"Toddler"} = 3;
$age{"Unborn"} = 0:
$age{"Phantasm"} = undef;
foreach $thing ("Toddler", "Unborn", "Phantasm", "Relic"} { print "$thing: ";
print "Exists " if exists $age{$thing};
print "Defined "if defined $age{thing}:
print "True " if $age{$thing};
print "\n";
}
Toddler: Exists Defined True Unborn: Exists Defined Phantasm: Exists Relic: Элемент $age{ "Toddler"} проходит все три проверки - существования, определенности и истинности. Он существует, потому что мы присвоили ключу "Toddler" значение в хэше. Он определен, потому что значение не равно undef. Наконец, он истинен, потому что прис Элемент $age{" Unborn"} проходит только проверки существования и определенности. Он существует, потому что ключу "Unborn" было присвоено значение в хэше, и определен, потому что это значение не равно undef. Однако он не является истинным, потому что 0 инт Элемент $age{ "Phantasm"} проходит только проверку существования. Он существует, потому что ключу "Phantasm" было присвоено значение в хэше. Поскольку это значение представляет собой undef, проверка определенности не работает. Так как undef также считаетс Наконец, $age{ "Relic"} не проходит ни одну из проверок. Значение для "Relic" не заносилось в хэш, поэтому проверка на существование завершается неудачей. Из-за отсутствия ассоциированного значения попытка обратиться к $age{ "Relic"} дает undef. Как мы зн Иногда undef полезно сохранить в хэше. Это означает: "такой ключ встречается, но с ним не связано никакого полезного значения". Например, рассмотрим программу, которая определяет размер файлов из переданного списка. Следующий фрагмент пытается пропускать

%name = ();
while (о) {
chomp;
next if $name{$_}; # НЕВЕРНО !
$name{$_} = -s $_;
}


Замена неправильной строки следующим вызовом exists позволяет пропускать нулевые и несуществующие файлы: next if exists $name{$_}; В самом первом примере предполагается, что все, что не является едой (food), относится к напиткам (dnnk). В реальном мире подобные допущения весьма опасны.

> Смотри также -------------------------------
Описание функций exists и defined в perlfunc(1). Концепция истинности рассматривается в разделе "Scalar Values" perldata(1).

5.3. Удаление из хэша

Проблема

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

Решение

Воспользуйтесь функцией delete: # Удалить $КЛЮЧ и ассоциированное значение из хэша %ХЭШ с)е1е1е($ХЭШ{$КЛЮЧ});

Комментарий

Многие ошибочно пытаются удалять элементы из хэша с помощью undef - undef ${ХЭШ{$КЛЮЧ} или $ХЭШ{$КЛЮЧ} = undef. В обоих случаях в хэше будет присутствовать элемент с ключом $КЛЮЧ и значением undef. Функция delete - единственное средство для удаления конкретных элементов из хэша. Удаленный элемент не появится ни в списке keys, ни в итерациях each; функция exists возвращает для него ложное значение. Следующий фрагмент демонстрирует отличия undef от delete:

# Хэш %food_color определяется во введении
sub print_foods {
my (°>foods = keys %food_color,
my $food;
print "Keys: @foods\n";
print "Values: ";
foreach $food (Ofoods) {
my $color = $food_color{$food};
if (defined $color) { print "$color ";
} else {
print "(undef)"; }
}
print \n";
}
print "Initially:\n";
print_foods();
print "\nWith Banana undef\n";
undef $food_color{"Banana"};
print_foods();
print "\nWith Banana deleted\n";
delete $food_color{"Banana"};
print_foods();


Initially:
Keys: Banana Apple Carrot Lemon Values: yellow red orange yellow
With Banana undef
Keys: Banana Apple Carrot Lemon
Values: (undef) red orange yellow
With Banana deleted Keys: Apple Carrot Lemon Values: red orange yellow Как видите, после присвоения $food_color{"Banana"} = undef ключ "Banana" остается в хэше. Элемент не удаляется; просто мы присвоили ему undef. С другой стороны, функция delete действительно удалила данные из хэша - ключ "Banana" исчезает из списка, возвра Функция delete также может вызываться для среза хэша, это приводит к удалению всех указанных ключей:
delete @food_color{"Banana", "Apple", "Cabbage"};


> Смотри также --------------------------------
Описание функций delete и keys в peiifunc(1). Применение keys продемонстрировано в рецепте 5.4.

5.4. Перебор хэша

Проблема

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

Решение

Воспользуйтесь функцией each в цикле while:
while(($Kлюч, $ЗНАЧЕНИЕ) = each(%ХЭШ)) { # Сделать что-то,с $КЛЮЧ и $ЗНАЧЕНИЕ
}
Если хэш не очень велик, можно вызвать keys в цикле fоreach:
foreach $КЛЮЧ (keys %ХЭШ) {
$ЗНАЧЕНИЕ = $ХЭШ{$КЛЮЧ};
# Сделать что-то с $КЛЮЧ и $ЗКАЧЕНИЕ }

Комментарий

Следующий простой пример перебирает элементы хэша %food_color из введения:
# Хэш %food_color определяется во введении
while(($food, $color.) = each(%food_color)) { print "$food is $color.\n";
}

Banana is yellow. Apple is red. Carrot is orange. Lemon is yellow. В примере с foreach можно обойтись без переменной $со1ос, поскольку она используется всего один раз. Достаточно написать:
print "Stood is $food_color{$food}.\n".

При каждом вызове each для одного и того же хэша функция возвращает "следующую" пару ключ/значение. Слово "следующую" взято в кавычки, потому что пары возвращаются в порядке, соответствующем внутренней структуре хэша, и этот порядок почти никогда не с В примере с foreach использована функция keys, которая строит список всех ключей из хэша еще перед началом выполнения цикла. Преимущество each заключается в том, что пары "ключ/значение" извлекаются по одной. Если хэш содержит много ключей, отказ от предв Применение foreach и keys для перебора списка позволяет установить свой порядок обработки. Предположим, нам понадобилось вывести содержимое хэша в алфавитном порядке ключей:
foreach $food (sort keys %food_color) { print "$food is $food_color{$food}.\n";
}

Apple is red. Banana is yellow. Carrot is orange. Lemon is yellow.

Подобное применение to reach встречается довольно часто. Функция keys строит список ключей в хэше, после чего to reach перебирает их. Если хэш состоит из большого числа элементов, возникает опасность, что возвращаемый keys список займет много памя Поскольку функции keys, values и each используют одни и те же внутренние структуры данных, следует внимательно следить за чередованием вызовов этих функций или преждевременным выходом из цикла each. При каждом вызове keys или values текущая позиция each с

while ( ($k,$v) = each %food_color) {
print "Processing $k\n";
keys %food_color; # Возврат к началу
%food_color }
Модификация хэша во время его перебора в each или f о reach, как правило, сопряжена с опасностью. При добавлении или удалении ключей из хэша функция each ведет себя по-разному для связанных и несвязанных хэшей. Цикл fо reach перебирает заранее построенный Программа countfrom из примера 5.1 читает файл почтового ящика и выводит количество сообщений от каждого отправителя. Отправитель определяется по строке From: (в этом отношении сценарий не очень интеллектуален, однако нас сейчас интересуют операции с хэша
Пример 5.1. countfrom

#!/usr/bin/perl
# countfrom - подсчет сообщений от каждого отправителя
$filename = $ARGV[0] || "-oo;
open(FILE, "<$filename") or die "can't open $filename : $!";
while() {
if (/"From: (.*)/) { $from{$1}++ }
}
toreach $person (sort keys %from) { print "$person: $from{$person}\n";


[> Смотри также --------------------------------
Описание функций each и keys в peiifunc(1), описание циклов for и foreach в рецепте 4.5.

5.5. Вывод содержимого хэша

Проблема

Требуется вывести содержимое хэша, однако конструкции print "%ХЭШ" и print %ХЭШ не работают.

Решение

Одно из возможных решений - перебрать все нары "ключ/значение" в хэше (см. рецепт 5.4) и вывести их:

while ( ($k,$v) = each %hash) { print "$k => $v\n";
}

Также можно построить список строк с помощью mар:
print тар { "$_ => $hash{$_}\n" } keys %hash;

Или воспользуйтесь фокусом из рецепта 1.10 и интерполируйте хэш как список:
print "@{[ %hash ]}\n";

Или сохраните хэш во временном массиве и выведите его:
{
my @temp = %hash;
print "@temp";
}

Комментарий

Все перечисленные приемы обладают различными возможностями по управлению порядком н форматированием вывода, а также различной эффективностью. Первый способ (перебор хэша) чрезвычайно гибок и эффективен но затратам памяти. Вы можете как угодно форматировать выходные данные, при этом понадобятся всего две скалярные переменные - текущий ключ и значение. Использование цикла foreach позволяет вывест
foreach $k (sort keys %hash) {
print "$k => $hash{$k}\n";
}
Функция map не уступает перебору по богатству возможностей. Сортировка ключей по-прежнему позволяет работать с элементами в произвольном порядке, Выходные данные можно как угодно срорматировать. На этот раз создастся список строк (например, "КЛЮЧ==>ЗНАЧЕНИЕ", как в приведенном выше примере), передаваемый print. Два последних приема представляют собой фокусы, связанные с интерполяцией. Интерпретация хэша как списка не позволяет предсказать или управлять порядком вывода пар "ключ/значение". Более того, данные в этом случае выводятся в виде списка ключей и значений

> Смотри также -------------------------------
Описание переменной $" в perlvar(1); описание функций foreach, map, keys, sort и each в perlfunc(1). Строковая интерполяция рассматривается в рецепте 1.10, а перебор хэша - в рецепте 5.4.

5.6. Перебор элементов хэша в порядке вставки

Проблема

Функции keys и each извлекают элементы хэша в довольно странном порядке. Вы хотите получить элементы в порядке вставки.

Решение


Воспользуйтесь модулем Tie::IxHash.
use Tie::IxHash;
tie %ХЭШ, "Tie::IxHash";
# Операции с хэшем %ХЭШ
@keys = keys %ХЭШ; # Массив @keys отсортирован в порядке вставки

Комментарий

Модуль Tie::IxHash заставляет функции keys, each и values возвращать элементы в порядке занесения в хэш. Это часто избавляет от необходимости заранее обрабатывать ключи хэша какой-нибудь сложной сортировкой или поддерживать отдельный массив, содержащий кл Tie::IxHash также представляет объектно-ориентированный интерфейс к функциям splice, push, pop, shift, unshift, keys, values и delete, а также многим другим. Следующий пример демонстрирует использование keys и each: # Инициализировать use Tie::IxHash;
tie %food_color, "Tie::IxHas";
$food_color{Banana} = "Yellow";
$food_color{Apple} = "Green";
$food_color{Lemon} = "Yellow";
print "In insertion order, the foods are:\n";
foreach $food (keys %food_color) { print " $food\n";
}
print "Still in insertion order, the foods' colors are:\n' while (( $food, $color ) =
each %food_color ) { print "$food is colored $color.\n":
}

In insertion order, the foods are:
Banana
Apple
Lemon Still in insertion order, the foods' colors are:
Banana is colored Yellow. Apple is colored Green. Lemon is colored Yellow.

> Смотри также ----------------
Документация по модулю Tie::IxHash от CPAN; рецепт 13.15.

5.7. Хэши с несколькими ассоциированными значениями

Проблема

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

Решение

Сохраните в хэше ссылку на массив для хранения ассоциированных значений.

Комментарий

В хэше могут храниться только скалярные величины. Однако ссылки являются скалярными величинами. Таким образом, проблема решается сохранением в $ХЭШ {$КЛЮЧ} ссылки на массив со значениями, ассоциированными с ключом $КЛЮЧ. Обычные операции с хэшами - вставк Следующий фрагмент реализует простую вставку в хэш. Он обрабатывает выходные данные команды who(i) на компьютере с UNIX и выводит краткий список пользователей с терминалами, на которых они зарегистрированы:
%ttys =();
open(WHO, "who|") or die "can't open who: $!";
while () {
($user, $tty) = split;
push( @{$ttys{$user}}, $tty );
}
foreach $user (sort keys %ttys) { print "$user: @{$ttys{$user}}\n";
}
Вся суть этого фрагмента заключена в строке push, где содержится версия $tty{$user} = $tty для многозначного хэша. Все имена терминалов интерполируются в строке print конструкцией @{$ttys{user}}. Если бы, например, нам потребовалось вывести владельца

foreach $user (sort keys %ttys) {
print "$user: ", scalar( @>{$ttys{$user}} ), "ttys.\n";
foreach $tty (sort @{$ttys{$user}}) {
@stat = stat("/dev/$tty");
$user = @stat ? ( getpwuid($stat[4]) )[0] : "(not available)";
print "\t$tty (owned by $user)\n";
}
}

Функция exists может иметь два значения: "Существует ли в хэше хотя бы одно значение для данного ключа?" и "Существует ли данное значение для данного ключа?" Чтобы реализовать вторую интерпретацию, придется просмотреть массив в поисках нужной вели
sub multihash_delete {
my {$hash, $key, $value) = @_;
my $i;
return unless ref( $hash->{$key} );
for ($i = 0; $i < @{ $hash->{$key} }; $i++) { if ($hash->{$key}->[$i] eq $value) {
splice( @{$hash->{$key}}, $i, 1);
last;
}
}
delete $hash->{$key} unless @{$hash->{$key}};
}


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

> Смотри также -------------------------------
Описание функций splice, delete, push, foreach и exists в perlfunc(1); рецепт 11.1. Связи рассматриваются в рецепте 13.15.

5.8. Инвертирование хэша

Проблема

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

Решение

Воспользуйтесь функцией reverse для создания инвертированного хэша, где ассоциированные значения исходного хэша являются ключами, и наоборот. # %ХЭШ связывает ключи со значениями %ОБРАТНЫЙ = reverse %ХЭШ;

Комментарий

В этом решении используется списковая эквивалентность хэшей, о которой упоминалось во введении. В списковом контексте reverse интерпретирует %ХЭШ как список и меняет местами составляющие его элементов. Одно из важнейших свойств списковой интерпретации хэш

%surname = ( "Mickey" => "Mantle", "Babe" => "Ruth");
%first_name = reverse %surname;
print $first_name{"Mantle", "\n"};

Mickey
Если интерпретировать %surname как список, мы получим следующее:
("Mickey", "Mantle", "Babe", "Ruth")
(а может быть, ("Babe", "Ruth", "Mickey", "Mantle"), поскольку порядок элементов
непредсказуем). После инвертирования список выглядит так:
("Ruth", "Babe", "Mantle", "Mickey") Интерпретация его в качестве хэша дает следующее:
("Ruth" => "Babe", "Mantle" => "Mickey") В примере 5.2 приведена программа foodfind. Если передать ей название продукта, она сообщает цвет, а если передать цвет - она сообщает название. Пример 5.2. foodfind

#!/usr/bin/perl -w
# foodfind - поиск продуктов по названию или цвету
$given = shift @ARGV or die "usage: foodfind food_or_color\n";
%color = (
"Apple" => "red",
"Banana" => "yellow", 'Lemon" => "yellow", 'Carrot" => "orange"
}
%food = reverse %color;
if (exists $color{$given}) {
print "$given is a food with color $color{$given}.\n";
} if (exists $food{$given}) {
print "$food{$given} is a food with color $given.\n";
}


Если два ключа исходного хэша имеют одинаковые значения ("Lemon" и "Banana" в предыдущем примере), то инвертированный хэш будет содержать лишь один из них (какой именно - зависит от порядка хэширования, так что непредсказуемо). Дело в том, что хэш Чтобы инвертировать хэш с повторяющимися значениями, следует воспользоваться методикой рецепта 5.7 - то есть построить хэш, ассоциированные значения которого представляют собой списки ключей исходного хэша:
# Хэш %food_color определяется во введении
while (($food,$color) = each(%food_color)) {
push(@{foods_with_color{$color}}, $food);
}
print "@{$foods_with_color{yellow}} were yellowfoods.n";

Banana Lemon were yellow foods, Кроме того, это позволит модифицировать программу foodfind так, чтобы она работала с цветами, соответствующими сразу нескольким продуктам. Например, при вызове foodfind yellow будут выводиться и Banana, и Lemon. Если какие-либо значения исходного хэша были не простыми строками и числами, а ссылками, при инвертировании возникает проблема - ссылки не могут использоваться в качестве ключей, если только вы не воспользуетесь модулем Tie::RefHash (см. рецепт 5.12).

> Смотри также
Описание функций reverse в perlfunc(t); рецепт 13.15.

5.9. Сортировка хэша

Проблема

Требуется работать с элементами хэша в определенном порядке.

Решение

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

# %hash - сортируемый хэш
@keys = sort { criterionO } (keys %hash);
foreach $key (Okeys) { $value = $hash{$key};
# Сделать что-то с $key, $value
}

Комментарий

Хотя хранить элементы хэша в заданном порядке невозможно (без использования модуля Tie:IxHash, упомянутого в рецепте 5.6), перебирать их можно в любом порядке. Существует множество разновидностей одного базового механизма: вы извлекаете ключи, упорядочиваете их функцией sort и обрабатываете элементы в новом порядке. Допускается применение любых хитростей сортировки, упоминавшихся в главе 4 "Массивы". Рассмотрим В первом фрагменте sort просто используется для упорядочения ключей по алфавиту:

foreach $food (sort keys %food_color) { print "$food is $food_color($food).\n":
} Другой фрагмент сортирует ключи по ассоциированным значениям:

foreach $food (sort { $food_color{$a} cmp $food_color{$b} } ) keys %food_color) {
print "$food is $food_color{$food}.\n";
}
Наконец, сортировка выполняется по длине ассоциированных значений:
@foods = sort { length($food_color{$a}) <=> length($food_color{$b}) }
keys %food_color;
foreach $food (Ofoods) {
print "$food is $food_color{$food}.\n":
}


> Смотри также --------------------------------
Описание функций sort и keys в perlfunc(1); рецепт 5.6. Сортировка списков рассматривается в рецепте 4.15.

5.10. Объединение хэшей

Проблема

Требуется создать новый хэш, содержащий элементы двух существующих хэшей.

Решение

Интерпретируйте хэши как списки и объедините их так, как это делается со списками:
%merged = (%A, %В);
Для экономии памяти можно организовать перебор элементов и построить новый хэш следующим образом:
%merged = ();
while ( ($k,$v) = each(%A) ) {
$merged{$k} = $v;
} while ( ($k,$v) = each(%B) ) {
$merged{$k} = $v;
}

Комментарий

В первом варианте, как и в предыдущем рецепте инвертирования хэшей, используется списковая эквивалентность, о которой говорилось во введении. (%A, %В) интерпретируется как список пар "ключ/значение". Когда он присваивается объединенному хэшу %merged, Perl
# Хэш %food_color определяется во Введении
%drink_color = ( Galliano => "yellow", "Mat Tai" => "blue" );
%ingested_colors = (%drink_color, %food_color);
Ключи обоих входных хэшей присутствуют в выходном не более одного раза. Если в хэшах найдутся совпадающие ключи, в итоговый хэш включается тот ключ, который встретился последним. Прямое присваивание компактно и наглядно, но при больших размерах хэшей оно приводит к большим расходам памяти. Это связано с тем, что перед выполнением присваивания итоговому хэшу Perl разворачивает оба хэша во временный список. Пошаговое объединение с п С применением each первый фрагмент записывается следующим образом:
# Хэш %food_color определяется во Введении
%drlnk_color = ( Galliano => "yellow", "Mat Tai" => "blue" );
%substance_color = ();
while (($k, $v) = each %food_color) { $substance_color{$k} = $v;
}
while (($k, $v) = each %drink_color) { $substance_color{$k} = $v;
}


Обратите внимание на повторяющийся код присваивания в циклах while. Проблема решается так:
foreach $substanceref (\%food_color, \%drink_color ) { while (($k, $v) = each v%substanceref) { $substance_color{$k} = $v;
}
}

vЕсли в объединяемых хэшах присутствуют одинаковые ключи, можно вставить код для обработки дубликатов:

foreach $substanceref (\%food_color, \%drink_color ) { while (($k, $v) = each
%substanceref) { if (exists $substance_color{$k}) {
print "Warning: $k seen twice. Using the first definition.\n";
next;
} $substance_color{$k} = $v;
}
}
В частном случае присоединения одного хэша к другому можно воспользоваться срезом для получения более элегантной записи:
@all_colors{keys %new_colors} = values %new_colors;
Потребуется память в объеме, достаточном для хранения списков всех ключей и значений %new_colors. Как и в первом варианте, расходы памяти при большом размере списков могут сделать эту методику неприемлемой.

> Смотри также -----------------------------
Описание функции each в perlfunc(1); рецепт 4.9.
© copyright 2000 Soft group

Используются технологии uCoz