Конструирование записей
Проблема
Требуется создать тип данных для хранения атрибутов (запись).
Решение
Воспользуйтесь ссылкой на анонимный хэш.
Комментарий
Предположим, вам захотелось создать тип данных, содержащий различные атрибуты — аналог
структур С или записей Pascal. Проще всего сделать это с помощью анонимного хэша. Следующий
пример демонстрирует процесс инициализации применения записи, содержащей информацию о работнике фирмы:
$record = {
NAME => "Jason",
EMPNO => 132,
TITLE => "deputy peon",
AGE => 23,
SALARY => 37_300,
PALS => [ "Norbert", "Rhys", "Phineas" ],
};
printf "I am %s, and my pals are %s.\n",
$record->{NAME}, join (",", @{$record->{PALS}});
Впрочем, от отдельной записи толку мало — хотелось бы построить структуры данных более
высокого уровня. Например, можно создать хэш %ByName, а затем инициализировать и использовать
его следующим образом:
# Сохранить запись
$byname{ $record->{NAME} } = $record;
# Позднее искать по имени
if ($rp = $byname{"Aron"}){ # false, если отсутствует
printf "Aron is employee %d.\n", $rp->{EMPNO};
}
# Дать Джейсону нового друга
push @{ $byname{ " Jason "}->{ PALS}}, "Theodore";
printf "Jason now has %d pals\n", scalar @{ $byname{" Jason" }->{PALS}};
В результате %byname превращается в хэш хэшей, поскольку хранящиеся в нем значения
представляют собой ссылки на хэши. Поиск работника по имени с применением такой структуры
оказывается простой задачей. Если значение найдено в хэше, мы сохраняем ссылку на запись
во временной переменной $rр, с помощью которой далее можно получить любое нужное поле.
Для операций с %byname можно использовать стандартные средства работы с хэшами.
Например, итератор each организует перебор элементов в произвольном порядке:
# Перебор всех записей
while (($name, $record) = each %byname) {
printf "%s is employee number %d\n",$name, $record->{EMPNO};
}
А как насчет поиска работников по номеру? Достаточно построить другу структуру данных —
массив хэшей @employees. Если работники нумеруются непоследовательно (скажем, после 1
следует номер 159997), выбор массива окажете неудачным. Вместо этого следует воспользоваться
хэшем, в котором номер работника ассоциируется с записью. Для последовательной
нумерации подойдет и массив:
# Сохранить запись
$employees[ $record->{EMPNO} ] = $record;
# Поиск по номеру
if ($rp = $employee[132]) {
printf "employee number 132 is %s\n", $rp->{NAME};
}
При работе с подобными структурами данных обновление записи в одном месте обновляет ее везде.
Например, следующая команда повышает жалование Джейсона на 3,5 %:
$byname{"Jason"}->{SALARY} *= 1.035;
Внесенные изменения отражаются во всех представлениях этих записей. Помните о том,
что $byname{"Jason"} и $employees[132] ссылаются на одну и ту же запись, поскольку хранящиеся в
них ссылки относятся к одному анонимному хэшу.
Как отобрать все записи, удовлетворяющие некоторому критерию? Для этого и была создана функция
grep.
Например, в следующем фрагменте отбираются два подмножества записей — работников, чья должность
содержит слово "peon", и тех, чей возраст равен 27 годам.
@peons = grep { $_->{TITLE} =~ /peon/i } (@employees;
@tsevens = grep { $_->{AGE) == 27 } @employees;
Каждый элемент @peons и @tsevens представляет собой ссылку на запись, поэтому они,
как и @employees, являются массивами хэшей.
Вывод записей в определенном порядке (например, по возрасту) выполняется так:
# Перебрать все записи
foreach $rp (sort { $a->{AGE} <=> $b->{AGE} } values %byname) {
printf "%s is age %d.\n", $rp->{NAME}, $rp->{AGE};
# или со срезом хэша через ссылку
printf "%s is employee number %d.\n", @$rp{ 'NAME', 'EMPNO'};
}
Вместо того чтобы тратить время на сортировку по возрасту, можно просто создать для
этих записей другое представление, @byage. Каждый элемент массива (например, $byage[27])
является массивом всех записей с данным возрастом. Фактически мы получаем массив массивов
хэшей. Он строится так:
# Используем @bуаgе, массив массивов записей
push @{ $byage[ $record->{AGE} ] }, $record;
Далее отбор осуществляется следующим образом:
for ($age = 0; $age <= $#byage; $age++) {
next unless $byage[$age];
print "Age $age: ";
foreach $rp (@{$byage[$age]}) {
print $rp->{NAME}, " ";
}
print "\n";
}
Аналогичное решение заключается в применении
map, что позволяет избежать цикла foreach:
for ($age = 0; $age <= $#byage; $age++) {
next unless $byage[$age];
printf "Age %d: %s\n", $age,
join (", ", map {$_->{NAME}} @{$byage[$age]});
}
См. также
Proverte kod v komentariyah gde pro list tam oshibki detskie
Оставить комментарий:
|
|