Конструирование записей

Проблема

Требуется создать тип данных для хранения атрибутов (запись).

Решение

Воспользуйтесь ссылкой на анонимный хэш.

Комментарий

Предположим, вам захотелось создать тип данных, содержащий различные атрибуты — аналог структур С или записей 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]});
}

См. также




2013-09-10 17:05:19

Proverte kod v komentariyah gde pro list tam oshibki detskie




Оставить комментарий:
Ваше Имя:
Email:
Антибот: *  
Ваш комментарий: