The OpenNET Project / Index page

[ новости /+++ | форум | теги | ]

Каталог документации / Раздел "Perl" / Оглавление документа

Хеши


Хэш - ассоциативный массив, т.к. доступ к данным осуществляется при помощи ключа, ассоциированного со значением.

Объявление

Хэши начинаются с префикса %: %hash. Для работы с с хэш-массивами нужно, как и в обычных массивах, использовать разыменовывающий префикс $: $hash{шляпа} = серая;

Хэш можно определить несколькими способами:
%hash = (
	'шляпа' => 'серая',
	'водка' => 'горькая',
	'вобла' => 'вкусная',
	'штаны' => 'широкие',
	'пиво' => 'темное',
	'игрушка' => 'любимая');


%hash = (); #можно не указывать пустой хеш для создания массива, perl может сам создавать пустой хэш.
$hash{'шляпа'} =
'серая';
$hash{'водка'} = 'горькая';
$hash{'вобла'} = 'вкусная';
$hash{'штаны'} = 'широкие';
$hash{'пиво'} = 'темное';
$hash{'игрушка'} = 'любимая';

Если используется пробел при определении элемента хэша, то этот пробел лучше поставить в одинарные кавычки $hash{'дырявая шляпа'} = 'серая';

Добавить элементы в хеш можно так:
%hash = (
	'шляпа' => 'серая',
	'водка' => 'горькая',
	'вобла' => 'вкусная');
%hash = (%hash, 'кошка', 'мягкая');
foreach $k (keys %hash){
print "$k = $hash{$k}\n";
}
и так:
%hash = (
	'шляпа' => 'серая',
	'водка' => 'горькая',
	'вобла' => 'вкусная');
$hash{"дом"} = "большой";
$hash{"дым"} = "сизый";
foreach $k (keys %hash){
print "$k = $hash{$k}\n";
}
и еще например так:

my %Years = ( 
      1999 => "golos1999.html", 
      2000 => "golos2000.html", 
      2001 => "golos2001.html",
    map{$_ => "golos$_.html"}(2002..2032)
            );


т.е. если у нас есть файлы, которые группируются по годам, дням, месяцам или еще как, то дабы не переписывать скрипт, можно сказать map { $_ => "golos$_.html" } (2002..2032) где год или неделя берется из какое-йнибудь переменной, в данном случае из $_.

доступ к элементам

Проверка хэша на наличие элемента:

%hash = (
	'шляпа' => 'серая',
	'водка' => 'горькая',
	'вобла' => 'вкусная');

if(exists($hash{"дождь"})){
	print "Элемент найден";
        }
else{
	print "Элемент не найден";
	}
Удалить элемент из хэша можно при помощи функции delete:

%hash = (
	'шляпа' => 'серая',
	'водка' => 'горькая',
	'вобла' => 'вкусная');

delete($hash{"шляпа"});
if(exists($hash{"шляпа"})){
	print "Элемент найден";
	}
else{
	print "Элемент не найден";
	}

Функция delete может вызываться для среза хэша, что приводит к удалению всех указанных ключей: delete @hash{'шляпа','водка','вобла'}; см. perlfunc(1)

Если нужно найти совпадающие ключи или не входящие в другй хэш, то надо организовать перебор ключей хэша при помощи keys и проверять, если ли текущий ключ в другом хэше. Поиск совпадающих ключей:


my @common = ();
foreach(keys %hash1){
	push(@common, $_) if exists $hash2{$_};

}

Поиск ключей, отсутствующих в другом хэше:

my @test = ();
foreach(keys %hash1){
	push(@test, $_) unless exists $hash2{$_};
}

Если keys вызывается для хэша, ключи которого представляют собой ссылки, то возвращаемые ей ссылки не работают. Ключи преобразуются в строки, т.е. интерпретируются так, словно они заключены в кавычки, при работе со ссылками они теряют свои свойства. После преобразования в строку ссылка имеет вид


Class::Somewhere=HASH(0x72048)
ARRAY(0x72048)

Преобразованную ссылку нельзя вернуть к прежнему виду, т.к. она из ссылки превратилась в строку. Нужно создать специальный хэш, ключами которого являются ссылки, преобразованные в строки, и значениями - настоящие ссылки.Можно воспользоваться модулем Tie::RefHash. Пример показывает использование объектов ввода/вывода для работы с файловыми манипуляторами.


use Tie::RefHash;
use IO::File;

tie %name, "Tie::RefHash";
foreach $filename("/etc/termcamp/", "/vminux", "/bin/cat"){
	$fh = IO::File->("<$filename") or next;
	$name{$fh} = $filename;
}
print "open files: ", join(", values %name", "\n");
foreach $file(keys %name){
	seek($file, 0, 2);
	printf("%s is %d bytes long.\n", $name{$file}, $tell{$file});
}

Если в качестве ключа использована неопределенная величина undef, то она преобразуется в пустую строку. undef является вполне допустимым значением в хэше. Но при выборке значения для ключа, отсутствующего в хэше perl выдаст undef. Проверить наличие ключа можно так: exist($hash{$key}); определенность ассоциированного значения: defined($hash{$key}); истинность: if($hash{$key});. Иногда undef нужно сохранять в кэше, т.е. ключ есть, но с ним не связано ничего полезного, например программа, определяющая размер файлов из переданного списка:


%name =();
while(<>){
	chomp;
	next if exist $name{$_};
	$name{$_} = -s $_;
}

Этот код позволяет пропустить несуществующие и нулевые файлы, но записанные в исходном списке.

Хэши с несколькими значениями, ассоциированными одним ключом. Т.к. скалярные величины, содержащиеся в хэше, могут быть ссылками(которые могут быть скалярами), то ассоциировать несколько значений одним ключом можно сохранив в $hash($key) ссылку на массив со значениями, ассоциированными с ключом $key. Операции с хэшами(вставка, удаление, перебор и проверка существования(undef)) переписываются для операций с массивами(push, splice и foreach). Пример, реализующий вставку в хэш(обрабатывает выходные данные команды who(1) и выводит краткий список пользователей с терминалами, на которых они зарегестрированы):


%ttys=();
open (WHO, "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}}.

Пример программы, которая на название предмета выдает его свойство и наоборот:


#!/usr/bin/perl -w 
$vziat = shift @ARGV or die $!;
%svojstvo = (
        "malina" => "vkusnaia",
        "svekla" => "krasnaya",
     "kozmodrom" => "nebolshoy",
       "magazin" => "dvuhetagnij");
%predmet = reverse %svojstvo;
if (exists $svojstvo{$vziat}){print "$vziat," ", $svojstvo{$vziat}\n";}
elsif (exists $predmet{$vziat}){print "$vziat," ", $predmet{$vziat}\n";}

например если ввести в терминале:
bash-2.03$ ./1.pl malina
то скрипт выдаст:
malina  vkusnaia
или
bash-2.03$ ./1.pl vkusnaia
vkusnaia  malina

В чем различие delete и undef для хешей?

Хеши являются парами скаляров, первый - ключ, второй значение. Ключ может быть строкой, в то время как значением хеша может быть любой вид скаляра: строка, число или ссылка. Если ключ содержится в хеше, то exists($key) возвратит истину. Значение для какого-то конкретного ключа может быть undef'ом, и $array{$key} возвратит так-же undef, но exists($key) возвратит истину. Иными словами в хеше может быть реализована связка ('$key', 'undef')

В качестве примера можно привести следующую таблицу %ary:

              keys  values
            +------+------+
            |  a   |  3   |
            |  x   |  7   |
            |  d   |  0   |
            |  e   |  2   |
            +------+------+
Этот хеш выглядит примерное так:
            $ary{'a'}                        true
            $ary{'d'}                        false
            defined $ary{'d'}                true
            defined $ary{'a'}                true
            exists $ary{'a'}                 true (perl5 only)
            grep ($_ eq 'a', keys %ary)      true
Если теперь сказать
            undef $ary{'a'}
То таблица будет читаться следующим образом:
              keys  values
            +------+------+
            |  a   | undef|
            |  x   |  7   |
            |  d   |  0   |
            |  e   |  2   |
            +------+------+

И теперь логические состояния в хеше уже немного другие, изменения показаны регистром
            $ary{'a'}                       FALSE
            $ary{'d'}                       false
            defined $ary{'d'}               true
            defined $ary{'a'}               FALSE
            exists $ary{'a'}                true (perl5 only)
            grep ($_ eq 'a', keys %ary)     is true
Отсюда следует вывод, что можно держать значение undef'ом, но ключ всегда должен быть определен.

Теперь рассмотрим операцию удаления элемента из хеша:

            delete $ary{'a'}
после этого таблица будет выглядеть так:
              keys  values
            +------+------+
            |  x   |  7   |
            |  d   |  0   |
            |  e   |  2   |
            +------+------+
Состояния элементов в хеше уже другие, изменения показаны, как и в предыдущем примере, различающимся регистром.
            $ary{'a'}                       is
false
            $ary{'d'}                       is false
            defined $ary{'d'}               is true
            defined $ary{'a'}               is false
            exists $ary{'a'}                is FALSE (perl5 only)
            grep ($_ eq 'a', keys %ary)     is FALSE


from: perlreply@faq-by-day.org

Вывод

Для того чтобы организовать циклы по элементам хэша, нужно использовать функцию each:

%hash = (
	'шляпа' => 'серая',
	'водка' => 'горькая',
	'вобла' => 'вкусная');

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


Для перебора элементов не очень большого хеша можно воспользоваться foreach:

%hash = (
	'шляпа' => 'серая',
	'водка' => 'горькая',
	'вобла' => 'вкусная');

foreach $key(keys %hash){
print $key,"\n"; #возвращает список ключей хеша
}

%hash = (
	'шляпа' => 'серая',
	'водка' => 'горькая',
	'вобла' => 'вкусная');    

foreach $value(values %hash){
print "$value\n"; #возвращает список значений хеша
}

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

Цикл foreach перебирает заранее построенный список ключей, поэтому после начала цикла он ничего не знает о добавленных или удаленных ключах, ключи, добавляемые внутри цикла, не включаются автоматически в список перебираемых ключей, а удаленные внутри цикла ключи не удаляются из этого списка.

Содержимое хэша можно вывести и так:
while (@Res = each %hash){
        print "$Res[0] = $Res[1]\n"
}

Вывод хэша одной строкой.

можно воспользоваться функцией map: print map {"$_ => $hash{$_}\n"} keys %hash;

функция map позволяет работать с элементами в произвольном порядке, в этом случае создается список строк(ключ => значение), передаваемый функции print. Если сохранить хэш во временном массиве, то он будет выведен как строка:


{
my @temp = %hash;
print "@temp";
}

также можно вывести хэш и таким образом: print "@{[%hash]}\n";

в двух последних случаях мы интерполируем хэш как список, что не позволяет предсказать или управлять порядком вывода пар "ключ/значение". Данные в последних двух случаях выводятся в виде списка ключей и значений, элементы которого разделяются текущим содержимым переменной $", т.е. не удастся вывести каждую пару значений на новой строке или отделить ключи от значений каким-либо символом. Приведем программу, которая читает файл почтового ящика и выводит количество сообщений от каждого отправителя, отправитель определяется по строке From(цель программы только проиллюстрировать операции с хешами):


#!/usr/bin/perl -w
$file = $ARGV[0] || "-";
open(FILE, "<$file") or die "$!";
while(<FILE>){
	if(/^From: (.*)/) { $from{$1}++ }
}
foreach $people(sort keys %from){
print "$people: $from{$people}\n";

}



Запускаем программу чтения почтового ящика: bash-2.03$ ./1.pl /usr/home/vovka/mbox Для того, чтобы подсчитать число ключей хеша, можно воспользоваться функцией scalar:

    $num_keys = scalar keys %hash;

Инвертирование хэша производится при помощи функции reverse, в котором ассоциированные значения исходного хэша являются ключами и наоборот. Воспользуемся списковой эквивалентностью хэшей. В списковом контексте reverse иетерпретирует %hash как список и меняет местами составляющие его элементов. Например: имеем хэш %Glavfish = ("seledka"=>"mokraia","skat"=>"elektricheskij"), если его интерпретировать как список, то получим следующее ("seledka","mokraia","skat","elektricheskij"), после инвертирования список выглядит так: ("elektricheskij","skat","mokraia","seledka"), интерпретация его как хэша дает следующее: ("elektricheskij"=>"skat","mokraia"=>"seledka").

Вывод хеша в порядке вставки с использованием Tie::IxHash

Для перебора элементов хэша в порядке вставки, т.к. keys и each выводят элементы хеша неупорядоченно, можно воспользоваться модулем(либо операциями с массивами) Tie::IxHash


use Tie::IxHash
tie %hash, "Tie::IxHash";
#операции с %hash
@keys = keys %hash;

Модуль Tie::IxHash заставляет функции keys, each и values возвращать элементы в порядке занесения их в хэш. Если у Вас нет такого модуля IxHash.pm то нужно зайти на CPAN/modules, найти его и установить, если у вас нет прав на установку библиотек, то в первой строчке скрипта нужно написать #!/put'/do/perl'a -wT -I/put'/do/nugnogo/modulia и установить модуль в Вышей домашней директории. Пример использования Tie::IxHash:


use Tie::IxHash

tie %hash, "Tie::IxHash";
%hash = (
	'шляпа' => 'серая',
	'водка' => 'горькая',
	'вобла' => 'вкусная');

print "В упорядоченной вставке список хеша такой:\n";
foreach $qwerty (keys %hash){
	print " $qwerty\n";
}

print "Кроме того, предметы обладают некоторыми свойствами:\n";
while(($predmet, $opredelenie) = each %hash){
print $predmet $opredelenie,"\n";
}

см. тех документацию по модулю Tie::IxHash.

Вывод хеша в порядке вставки без использования Tie::IxHash

Без модуля Tie::IxHash вывод хеша в порядке вставки можно сделать при помощи дописывания числовой информации в хеш. Есть файл news.dat, который выводится скриптом в таком порядке, в каком данные занесены в файл. Необходимо удалить одновременно больше одной строчки из текста(не важно строка это, или разделитесь, это однозначно определяется переменной $/).

#!/usr/bin/perl -wT
use CGI 'param';
@del=param;
sub del{
  pop @del; $mass=~s!"|&(.*?);!!g;
  open F, "<news.dat" or die "Err: $!"; @mass=<F>; close F;
  open F, ">news.dat";
  foreach $un(@mass){ $as=$un; $i++; chomp $as; 
    $un=~s|(.*)new>(.*?)</a>(.*)\[(.*?)\]|$2$4|i;
    $un=~s!"|&(.*?);!!g; chomp $un;
	$u{"$un"}="$as#$i#\n";
  }
  foreach $del(@del){
    $del=~s!"|&(.*?);!!g; chomp $del;
    $terr="Link $u{$del} was deleted<p>\n" if (exists $u{"$del"}); 
    $terr=~s!\d{8}|#(.*?)#!!ig;
    print $terr; $terr="";
    delete $u{$del} if (exists $u{"$del"});
  }{ local $_;
  while (($km, $_) = each %u){ push @tmp, "$u{$km}"}
  }

  @temp=grep{s/#(\d+?)#//}
        map{ $_ -> [1]}
        sort{$a->[0] <=> $b->[0]}
        map{[/#(\d+?)#/, $_]} 
        grep{!$_{$_}++} @tmp;
  print F reverse @temp;
  close F;
}


Разъясним принцип работы скрипта. Исходная задача такова: на входе есть несколько checkbox из формы, в которых может быть поставлено больше одной галочки. Требуется найти и вычеркнуть отмеченные строчки. Файл news.dat модержит строки вида: 12345678<a href="lalalal">tra-ta-ta</a>&nbsp;&nbsp;&nbsp;[AAA] чекбокс отмечается текстом tra-ta-ta, т.е. что-то вида

  for ($i=$pos; $i<$pos+$n; $i++) { 
    $res[$i]=~s|^(\d\d\d\d)(\d\d)(\d\d)|$3\.$2\.$1 |;
    print qq~<tr><td>$res[$i]</td><td>
	<input type=checkbox name="$1$3"
value="$1$3"></td></tr>~
            if($res[$i]=~m!>(.*?)</a>(.*?)\[(.*?)\]!);
  }


т.е. name="$1$3" value="$1$3" => name=tra-ta-ta&value=tra-ta-ta. Идея заключается в том, что элементы хеша можно пронумеровать в исходном порядке вставки, который будет исходным в силу того, что хеш определяется foreach, который последовательно читает данные из массива. поэтому говорим $i++; в цикле, ставим цифру в разделителе #\d+# и получаем на выходе хеш:

  foreach $un(@mass){ $as=$un; $i++; chomp $as; 
    $un=~s|(.*)new>(.*?)</a>(.*)\[(.*?)\]|$2$4|i;
    $un=~s!"|&(.*?);!!g; chomp $un;
    $u{"$un"}="$as#$i#\n";
  }
Дальше начинаем в хеше искать данные, которые передались через @del=param;
  foreach $del(@del){
    $del=~s!"|&(.*?);!!g; chomp $del;
    $terr="Link $u{$del} was deleted<p>\n" if (exists $u{"$del"}); 
    $terr=~s!\d{8}|#(.*?)#!!ig;
    print $terr; $terr="";
    delete $u{$del} if (exists $u{"$del"});
  }

при помощи функции exists проводится проверка на наличие элемента в хеше. Итак, получили хеш с ключами, являющимися подстроками строк из файла news.dat, и значениями самих строк, т.е. в памяти точно лежит файл, превосходящий по размеру news.dat чуть меньше чем в два раза. Далее идет вытаскивание значений из файла, уже без удаленных(было сравнение по подстроке):

  { local $_;
  while (($km, $_) = each %u){ push @tmp, "$u{$km}"}
  }

Замечательно, проверили, занесли в массив @tmp. Здесь локализация local $_; применена для того, чтобы убрать при использовании ключа -w лишнего warning из серверного лог-файла ошибок. Вытащили новый массив, который нужно соранить в файл news.dat. Теперь нужно убрать из массива @tmp повторяющиеся элементы, отсортировать по номерам #(\d+)#, убрать эти номера из элементов массива @tmp и сохранить массив в прежнем виде:

  @temp=grep{s/#(\d+?)#//}
        map{ $_ -> [1]}
        sort{$a->[0] <=> $b->[0]}
        map{[/#(\d+?)#/, $_]} 
        grep{!$_{$_}++} @tmp;
  print F reverse @temp;

операция grep{!$_{$_}++} удаляет из массива повторяющиеся элементы, map{[/#(\d+?)#/, $_]} создает временный список анонимных массивов, которые затем сортируются sort{$a->[0] <=> $b->[0]}, затем map{ $_ -> [1]} приводит элементы массива в удобоваримый вид и grep{s/#(\d+?)#//} вырезает нуумерацию массива, оставшуюся от начального формирования хеша %u. Далее оборачиваем конечный массив @temp функцией reverse и получам такой-же файл news.dat, только без элементов, отмеченных пользователем в чекбоксе.

Еще один вывод хеша в порядке вставки без использования приспособленных для этого модулей:


my @qq = qw(a s d f g h j y f d e e t y u i v f s a e);
my @del = qw(f h u);
my (%to, %del, %exist);
map {$del{$_} = 1} @del;
for (my $i=$#qq; $i>=0; $i--){
  if (!exists $exist{$qq[$i]}){
    $exist{$qq[$i]} = 1;
    $to{$i} = $qq[$i] unless(exists $del{$qq[$i]});
  }
}
my @tmp;
foreach (sort{$a<=>$b} keys %to){
  push @tmp, $to{$_};
  print "$to{$_}\n";
}

автор: Monax from http://www.rt.mipt.ru/board

сортировки

Сортировка хэша:

%hash = (
	'шляпа' => 'серая',
	'водка' => 'горькая',
	'вобла' => 'вкусная');

foreach $key(sort keys %hash){
	print "$key => $hash{$key}\n"; #отсортирует в алфавитном порядке
по значениям ключа
}

foreach $value(sort values %hash){
	print "$value\n"; #сортировка по значению
}


Сортировка ключей по алфавиту ассоциированных значений:

foreach $key(sort {$hash{$a} cmp $hash{$b}} keys %hash){
        print $key, " => ", $hash{$key},"\n";
}
Сортировка по длинне ассоциированных значений:

@massive = sort{length($hash{$a}) <=> length($hash{$b})}
	keys %hash;
foreach $key(@massive){
	print $key, $hash{$key},"\n";
}

Пример из fido7.ru.perl:
 >> Есть хеш массив (слово, частота), необходимо вывести в файл
пары
 >> (слово,частота) отсортированные по частоте. По ключю - просто, а
вот
 >> по значению затрудняюсь. Подскахите как проще сделать?

 my %hash=('for'=>1000,'to'=>1500,'in'=>2000,'do'=>500);
 foreach(sort {$hash{$a} <=> $hash{$b}} keys %hash) {
     print $_,'=',$hash{$_},"\n";
     }
Как можно хеш положить в строку? Например проблема:

tnc> имеются логи http-gw. Плохость их
tnc> заключается в том, что на каждый запрос в логе появляется 4 строки,
tnc> совершенно не обязательно идущие подряд:

Создаешь хэш и держишь в нем те строчки, которые еще, грубо говоря, не закрыты, а как закроешь - удаляй. Конечно, некий заметный кусок файла в памяти будет присутствовать, но не полностью же.

Что-нибудь типа

while(<IN>) {
  ($key, $no, $data) = parse($_); # расковыряли строчку
  $buf{$key}-<[$no] = $data; # запихнули в хэш в виде массива
  next if $no > 3; # нумеруем, ессно с нуля
  analyze($buf{$key}); # обработали
  delete $buf{key}; # удалили
}

Слияние хешей выполняется как и слияние массивов:

%hash1 = (
	'шляпа' => 'серая',
	'водка' => 'горькая',
	'вобла' => 'вкусная');
%hash2 = (
	'штаны' => 'широкие',
	'пиво' => 'темное',
	'игрушка' => 'любимая');
%allhash = (%hash1, %hash2);


Чтобы сэкономить память, можно воспользоваться таким кодом:

%hash=(); 
while (($key, $values) = each(%hash1)){
	$hash{$key} = $values;
	}
while (($key, $values) = each(%hash2)){
	$hash{$key} = $values;
	}

Пример работы с хэшем и базой данных из fido7.ru.perl (cм. ответ):
VS>  Скажите, можно ли ввести в dbm-базу данных уже заполненный
значениями
VS> хеш? Если да, то как?


 %hash1=("dfadf", "dfadfd");
 dbmopen (%hash, "hash", 0666);
 %hash=%hash1;
 dbmclose (%hash);
Как передать хэш в функцию? fido7.ru.perl (см. всю дискуссию)
 >  PK> Вобщем то все передаеться, но
использовать его нельзяю
 >  PK> Hа %_[0] или $_[0]{??} компилер ругаеться.

 > %a;
 > &f(\%a);
 > sub f {
 >     my $a = $($_[0]){'key'};
 > }
 > кажется так, мог ошибится в написании. Смысл в передаче ссылки, а не
значения.
 
  Это можно сделать также:
 1. Неявной передачей ссылки (использование прототипов):
 sub f(\%) {
   my $a=$_[0]->{'key'};
 }
 f(%a);

 2. Передачей хэша как массива с четным числом
 элементов:
 sub f {
     my $a={@_}->{'key'};
 }
 f(%a);
Пример использования хеша для транслитерации fido7.ru.perl:
RK> Как сочнить такой tr/../../ (и возможно
ли), чтобы
 RK> "аб?я" -> "abyoya"
 RK> Т.е. транслитерацию сделать...

самый простой и топорный вариант - сделать хеш с соответствиями типа я => ya сплитить строчки и заменять элементы полученного массива на нужные. что-то на подобие:


@letters = split //, $str;
for (@letters){$_ = $hash{$_}};

встроенные хеши

Переменные окружения, использующие встроенные хэши %SIG, %ENV, %FORM{}.

%SIG - хэш, в котором хранятся обработчики различных ситуаций, возникающих в perl. Например строка local $SIG{__WARN__} = sub{}; отключает предупреждающие сообщения.

%ENV содержит значения переменных среды(окружения), заданных на момент запуска сценария(скрипта). Ключами обычно бывают имена переменных среды(но их состав зависит от операционной системы), изменение этих значений вызовет изменение окружения для процессов потомков.


#!/usr/bin/perl/ -w while (($key, $value) = each(%ENV)){ print "$key => $value\n"; }
программа выдает:

SERVER_SOFTWARE => Apache/1.3.11 (FreeBSD) mod_perl/1.21 PHP/3.0.14 
GATEWAY_INTERFACE => CGI/1.1 
DOCUMENT_ROOT => /usr/local/www/data 
UNIQUE_ID => OZaSFsHofQoAAEd@Cn8 
REMOTE_ADDR => 195.202.122.14 
SERVER_PROTOCOL => HTTP/1.0 
SERVER_SIGNATURE => Apache/1.3.11 Server at www.mojdodir.ru Port 80
REQUEST_METHOD => GET 
REMOTE_HOST => www.mojdodir.ru
QUERY_STRING => 

HTTP_USER_AGENT => Mozilla/4.73 [en] (Win98; I) 
PATH => /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin 
HTTP_ACCEPT => image/gif, image/x-xbitmap, image/jpeg, image/pjpeg,
image/png, */* 
HTTP_CONNECTION => keep-alive 
REMOTE_PORT => 3633 
SERVER_ADDR => 195.202.122.14 
HTTP_ACCEPT_LANGUAGE => en,ru 
HTTP_CACHE_CONTROL => max-age=259200 
SCRIPT_NAME => /cgi-bin/1.pl 
SCRIPT_FILENAME => /usr/local/www/cgi-bin/1.pl 
HTTP_ACCEPT_ENCODING => gzip 
SERVER_NAME => www.mojdodir.ru
HTTP_PRAGMA => no-cache 
REQUEST_URI => /cgi-bin/1.pl 
HTTP_ACCEPT_CHARSET => iso-8859-1,*,utf-8 
HTTP_X_FORWARDED_FOR => 15.0.0.23 
SERVER_PORT => 30 
HTTP_HOST => www.mojdodir.ru 
SERVER_ADMIN => webmaster@www.mojdodir.ru 
HTTP_VIA => 1.0 cache.www.mojdodir.ru:3363 (Squid/2.3.STABLE1) 


Непосредственно из скрипта элементы хэша %ENV можно вызывать $ENV{'HTTP_CACHE_CONTROL'} или $ENV{'HTTP_USER_AGENT'}, смотря что нужно вызывать.

%FORM содержит данные, вводимые из формы методом POST: html форма такая:

<form action="/cgi-bin/1.pl" method="post">
<input type="text" name="name1" size=10 maxlength=10>
<input type="text" name="name2" size=10 maxlength=10>
<input type="text" name="name3" size=10 maxlength=10>
<input type="submit" value="send">
<input type="reset" value="reset"></form>

Если мы введем в поле name1 qwe, name2 rty, name3 asd и нажмем send, то через STDIN передаются данные в виде: name1=qwe&name2=rty&name3=asd и содержимое хэша


%FORM( 
	name1 => qwe,
	name2 => rty,
	name3 => asd);
Значения полей name* можно получать $FORM{'name1'}, $FORM{'name2'} и т.д.

хэши slice

(перевод, статья находится по адресу http://www.sysarch.com/perl/tutorials/hash_slices.txt)
Насколько хорошим является этот код??

@array = qw( a b c d ) ;
@array{ @array } = ( [ @array ] ) x @array ;

Основное определение хэша slice: @hash{ @array } = ( list ) что эквивалентно ( $hash{ $array[0] }, ... $hash{ $array[$#array] } ) = ( list ) Префиксный символ (@ ,%) используется только для обозначения контекста, но не типа данных. Если имеются два массива и мы ходим сопоставлять их данные, то мы поступаем так:


@foo_array = qw( abc def ghi ) ;
@bar_array = qw( jkl mno pqr ) ;
@foo_to_bar{ @foo_array } = @bar_array

теперь можно работать с массивами немного проще:

$foo_value = 'def' ;
$bar_value = $foo_to_bar{ $foo_value } ;

Можно даже сразу преобразовывать целый массив переменных foo одной конструкцией: @bar_values = @foo_to_bar{ @foo_values }; Еще одно использование хэша slice, производится проверка: находится ли строка в данном массиве строк? Фактически мы не заботимся о том, что находится в хеше slice, но 1 используется для большей ясности.

@foo_array = qw( abc def ghi ) ;
@is_a_foo{ @foo_array } = (1) x @foo_array ;
$input = 'def' ;
if ( $is_a_foo{ $input } ) {
        ...
if ( exists( $is_a_foo{ $input } ) ) {
        ...

Оператор x называется оператором повторения, он дублирует левый операнд значением правого операнда. В скалярном контексте он копирует левый оперант как строку и возвращает его, т.е. мы имеем контекст списка и левый операнд списка. Это позволяет создать новый список с N копиями предыдущего списка. Для foo N = 3 и (скалярное значение @foo_array) и мы получаем список (1, 1, 1) который присваивается хешу slice. Программа, преобразующая строки в нумерованные индексные значения. Используется оператор диапазона (..), создающий список целых чисел, которые присваиваются хэшу slise:

@foo_array = qw( abc def ghi ) ;
@foo_to_index{ @foo_array } = ( 0 .. $#foo_array ) ;
@foo_to_index{ @foo_array } = ( 1 .. @foo_array ) ;
$i_am_a_foo = 'def' ;
$foo_index = $foo_to_index{ $i_am_a_foo } ;

Названия %foo_to_bar, %is_a_foo, $foo_to_index подобраны так, чтобы нагляднее показать принцип работы slise. Фактически была проведена индексация входных данных и выходных данных в хеш splice. Теперь рассмотрим хэш splice снова @array{ @array } = ( [ @array ] ) x @array ;. Заметим, что хэш и массив(и скаляр) называются массивами, но это различные переменные, в частности отличные по разделитеям. Левая часть выражения - присваивание хэшу slice, но что-же стоит справа? Оператор x и @array справа дублирует повторения счетчика и некоторый лист слева и мы имеем контекстный список. Хеш slice есть список контекстов, мы создаем скопированный список анонимного массива, который содержит значения в @array. Это значит что хэш %array напоминает такой массив:


%array = (
        'a' => [ 'a', 'b', 'c', 'd' ],  
        'b' => [ 'a', 'b', 'c', 'd' ],  
        'c' => [ 'a', 'b', 'c', 'd' ],  
        'd' => [ 'a', 'b', 'c', 'd' ],  
) ;

Если имеется набор псевдонимов(alias), то можно, обращаясь к списку, вывести каждый из них в отдельности(ср. alias для сокращения часто употребляемой команды в unix). Т.е. вводится любой из отдельных элементов и мы получаем список данных. Если имеется список массивов, то их можно обработать таким образом:

@foo_list = qw( a b c d );
@bar_list = qw( j k l m n o );
@baz_list = qw( w x );

@expand_aliases{ @foo_list } = ( [ @foo_list ] ) x @foo_list;
@expand_aliases{ @bar_list } = ( [ @bar_list ] ) x @bar_list;
@expand_aliases{ @baz_list } = ( [ @baz_list ] ) x @baz_list;

Если есть лексемма неопределенного типа, то можно получить список псевдонимов за 1 шаг @aliases = @{ $expand_aliases{ $alias } } ; Окружение @{} используется, чтобы разыменовать сохраненный анонимный список в список для присвоения @aliases.

есть хэш, типа

$myhash{name}=qwert;
$myhash{age}=15;
как сделать автоматическое создание переменной=ключу хэша со значением из этого хэша с этим же ключом, т.е. $name=qwert; $age=15 и т.д.

либо так:

my %myhash;
$myhash{name}='qwert';
$myhash{age}=15;
for $abc (keys %myhash)
  {
    $$abc=$myhash{$abc};
  }
print "\$name=$name; \$age=$age\n и т.д.";
либо так:
$$_ = $myhash{$_} for keys %myhash;
что в принципе одно и то-же... from: fido7.ru.perl

Объявление анонимного хеша:

Ссылки на существующие данные часто применяются для передачи аргументов функции, но в динамическом программировании они бывают неудобны. Для любой переменной есть два параметра, е? имя и адрес области памяти, где хранится эта переменная. Скалярная величина, хранящая адрес области памяти, назвается ссылкой. Значение, хранящееся в памяти по данному адресу называется субъектом(referent).
Анонимные хеши в perl могут создаваться явно. При этом выделяется память для хеша и возвращается ссылка на не?.

  $hash{map{$_ => $_*$_}(0 .. 4)}
  while(($k, $v) = each %$hash){
    print "$k => $v\n";
  }
  print $hash->{"3"}, "\n";

в цикле использовалась ссылка $hash на анонимный хеш. Обратиться к нужному элементу хеша можно и при помощи ассоциативного оператора стрелка ->: print $hash->{"3"}, "\n"; Пример использования анонимного хеша для выкидывания повторяющихся элементов из массива:

keys %{{ map { $_, 1 } @array }} # @array ->
anonymous hash -> keys
Источник: Преф-Ньюс

хеши хешей

Хеши, составленные из других хэшей, полезны при работе с текстовой многоуровневой информационной системой (например, экспертной системой). В этих случаях текстовые ключи используются для последовательного просмотра различных уровней структуры данных. В следующем примере такой хэш создается за раз:

%hash = (
      fruits => {
                favorite => "apples",
       'second favorite' => "oranges"
      },
  vegetables => {
                favorite => "corn",
       'second favorite' => "peas",
         'last favorite' => "turnip"
      },
        meat => {
                favorite => "chiken",
       'second favorite' => "beef"
      }

);
print $hahs{fruits}{favorite};

Обратите внимание, что в таком хэше значениями для пар ключ/значение высупают другие хэши(точнее, ссылки на них). Кроме того, для конструкций типа {...}{...}, между парами фигурных скобок неявно подставляется оператор-стрелка -> - разыменования ссылок,

Создание хэша хэшей на лету:

Чтобы создать хэш хэшей элемент за элементом, используется та же схема, что и в случае массива массивов, но с одним существенным отличием ? к хэшу всегда можно добавить новые ключ и значение, но добавление к массиву новых элементов с пропусками индексов порождает неопределенные значения. Пример:

%hash{fruits} = {favorite => "apples", 
        'second favorite' => "oranges"};

%hash{vegetables} = {favorite => "corn", 
            'second favorite' => "peas", 
             'least favorite' => "turnip"};

%hash{meat} = {favorite => "chicken", 
      'second favorite' => "beef"};

print $hash{fruits}{favorite};

В следующей схеме генератор анонимного хэша комбинируется со списком ключ/значение, возвращаемым внешней подпрограммой:

for $key ("hash1", "hash2", "hash3") 
   { $hash{$key} = {$returnlist}; }

sub returnlist{

return (key1 => valuel, key2 => value2);
}

print $hash{hash1}{key2};

Доступ к элеметнам хеша хешей

Чтобы получить отдельное значение, хранящееся в хэше хэшей, надо явно указать набор последовательных ключей:

%hash = (
     fruits => {favorite => "apples", 
       'second favorite' => "oranges"},

 vegetables => {favorite => "corn", 
       "second favorite' => "peas", 
        "least favorite' => "turnip"} 
);

print $hash{fruits}{"second favorite'};

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

%hash = (
     fruits => {favorite => "apples", 
       'second favorite' => "oranges"},

 vegetables => {favorite => "corn", 
       "second favorite' => "peas"} 
);

for $food (keys %hash) { 
  print "$food:\n\t {"; 
  for $key (keys %{$hash{$food}}) {
    print "'$key1' => \"$hash{$food}{$key}\",";
  }
  print "}\n";
} 

Чтобы сортировать записи хэш-таблицы по ключам, в заголовок цикла можно включить операцию сортировки:

%hash = (
      fruits => {favorite => "apples", 
                   second => "oranges"}, 
  vegetables => {favorite => "corn", 
                   second => "peas"}
);

for $food (keys %hash) { 
  print "$food:\n\t {"; 
  for $key (keys %{$hash{$food}}) {
    print "'$key1' => \"$hash{$food}{$key}\",";
  }
  print "}\n";
} 

массивы хэшей

Массивы хэш-таблиц позволяют индексировать числовым значением записи с именованными полями. В следующем примере создается массив хэшей:

@array = (
    {
           favorite => "apples",
  'second favorite' => "оranges"
    },
    {
           favorite => "corn",
  'second favorite' => "peas",
    'last favorite' => "turnip"
    },
    {
           favorite => "chiken",
  'second favorite' => "beef"
    }    
print $array[0]{favorite};

Обратите внимание, что для конструкций вида [...]{...}, как и для рассматриваемых ранее конструкций вида {...}{...} и [???][[???], между парами скобок неявно подставляется оператор-стрелка -> разыменования ссылок.

Создание массива хэшей ?на лету?

Можно создавать массивы хэшей шаг за шагом, присваивая ссылки на анонимные хэши элементам массива:

@аггау[0] = {favorite => "apples",
    'second favorite' => "oranges"};

@array[1] = {favorite => "corn",
    'second favorite' => "peas", 
     'least favorite' => "turnip"};

@array[2] = {favorite => "chicken",
    'second favorite' => "beef"};

print $array[0]{favorite};

Как и в случае массива массивов, вы можете воспользоваться функцией push:

push @array, {favorite => "apples",
     'second favorite' => "oranges"};

push @array, {favorite => "corn",
     'second favorite' => "peas",
      'least favorite' => "turnip"};

push @array, {favorite => "chicken",
     'second favorite' => "beef"};

print $array[0]{favorite};

В следующем примере мы последовательно читаем из текстовых строк пары ключ/значение и превращаем их в массив хэшей:

$data[0] = "favorite:apples, second
favorite:оranges";
$data[1] = "favorite:corn, second favorite:peas, least favorite:turnip";
$data[2] = "favorite:chicken, second favorite:beef";
for $loopindex (O..$#data) {
  for $element(split ',', $data[$loopindex]){
    ($key, $value) = split ':', $element;
    $key=~s/^[\s\n]+//; #очистить от пробелов
    $key=~s/[\s\n]+$//;
    $value =~s/^[\s\n]+//;  #очистить от пробелов
    $value =~s/[\s\n]+$//;
    $array[$loopindex]{$key} = $value;
  }
}
print $array[0]{'second favorite'};

Обратите внимание, что мы здесь воспользовались контекстно-чувствительной процедурой автооживления ссылок (autovivification)

Доступ к элементам массива хэшей

Чтобы получить значение, хранимое; в массиве хэшей, надо указать индекс массива и ключ хэша:

$array[0] = {favorite => "apples",
    'second favorite' => "oranges"};

$array[1] = {favorite => "corn",
    'second favorite' => "peas",
     'least favorite' => "turnip"};

$array[2] = {favorite => "chicken",
    'second favorite' => "beef"};

print $array[0]{favorite};

В следующем случае мы полностью выводим массив хэшей с помощью цикла по его элементам:

$array[0] = {favorite => "apples",
               second => "oranges"};

$array[1] = {favorite => "corn",
               second => "peas",
                least => "turnip"};

$array[2] = {favorite => "chicken",
               second => "beef"};

for $loopindex (0..$#array) {
  print "array[$loopindex]:\n\t{";
  fоr $key (keys %{$array[$loopindex]})
    { 
      print "$key => $array[$loopindex]{$key},"; 
    }
  print "}\n";

A вот как сделать то же самое, используя вместо индекса цикла ссылку:

$array[0] = {favorite => "apples",
               second => "oranges"};

$array[1] = {favorite => "corn",
               second => "peas",
                least => "turnip"};

$array[2] = {favorite => "chicken",
               second => "beef"};

for $hashreference(@array) {
  print "{";
  for $key (sort keys %$hashreference) {
    print "$key => $array[$loopindex]{$key}, ";
  }
print ,"}\n";
}

хэши массивов

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

%hasn = (
     fruits => ["apples", "oranges"], 
 vegetables => ["corn", "peas", "turnips"], 
       meat => ["chicken", "ham"],
);
print $hash{fruits}[0];

Обратите внимание, что для конструкций вида [...]{...}, как и для рассматриваемых ранее конструкций вида {...}{...} и [???][[???], между парами скобок неявно подставляется оператор-стрелка -> разыменования ссылок.

Создание хэша массивов ?на лету?

Чтобы собрать хэш массивов из отдельных элементов, можно заносить в хэш под нужным ключом ссылки на массивы, созданные генератором анонимных массивов:

%hash{fruits} = ["apples", "oranges"];
%hash{vegetables} = ["corn", "peas", "turnips"];
%hash{meat} = ["chicken", "ham"];
print $hash{fruits}[0];

Если вы предпочитаете другой вариант, можете воспользоваться функцией push и контекстно-чувствительной процедурой автооживления ссылок (autovivification).

push
@{%hash{fruits}}, "apples", "oranges";
push @{%hash{vegetables}}, "corn", "peas", "turnips";
push @{%hash{meat}}, "chicken", "ham";

Доступ к элементам хэша массивов

Вы всегда можете получить доступ к отдельному элементу данных, хранимому в хэше массивов, указав ключ и индекс:

%hasn = (
     fruits => ["apples", "oranges"], 
 vegetables => ["corn", "peas", "turnips"], 
       meat => ["chicken", "ham"],
);
print $hash{fruits}[0];

В следующем примере мы полностью выводим отсортированный по значениям ключа хэш массивов, используя функцию join для преобразования массивов в текстовые строки:

%hasn = (
     fruits => ["apples", "oranges"], 
 vegetables => ["corn", "peas", "turnips"], 
       meat => ["chicken", "ham"],
);

for $key(sort keys %hash){
  print "$key\t[", join(", ", @{$hash{$key}}), "]\n";
}

Хэш хешей массивов

Допустим в информации есть двойная степень вложенности, т.е. допустим 20 строк air и в этих 20 строках есть подкатегории по номерам, т.е. 20 строк состоят из трехподгрупп air,mumbra, air,kukumbra и air,telepuzikoff(on):



air,Acmar,one
air,Acmar,two
air,Airwell,one
air,Airwell,two
air,Ariston,one
air,Ariston,two
air,Ariston,three
air,Ariston,four
air,Mumbra,one
air,Mumbra,two
fridg,Ardor,one
fridg,Ardor,two
fridg,Ardor,three
fridg,Ardo-Young,one
fridg,Ardo-Young,two
fridg,Ardo-Young,three
wei,Tefal,one
wei,Tefal,two
wei,Tefal,three

Структурировать такую информацию можно при помощи гибрида хеша хешей и хеша массивов, т.е. хеша хешей массивов:

#!/usr/bin/perl
  
open F, "<example" or die "can open: $!\n"; @mass=<F>; close F;

for $gr(grep{!$_{$_}++} map{/^(.*?),/} @mass){
  for $line(@mass){push @{$hash{$gr}{$1}}, $2 if $line=~m!^$gr,(.*?),(.*)!}
}

print "name hash\tkeys\t\tmassives\n\n";
for $a(sort keys %hash){
  print "hash $a: \n\t{\n\t";
  for $key(sort keys %{$hash{$a}}){
    print "'$key' \t => [ ";
    print join " | " => @{$hash{$a}{$key}};
    print " ]\n\t";
  }
  print "}\n";
}

на выходе такая программа выдаст:
name hash	keys		massives

hash air: 
	{
	'Acmar' 	 => [ one | two ]
	'Airwell'	 => [ one | two ]
	'Ariston'	 => [ one | two | three | four ]
	'Mumbra'	 => [ one | two ]
	}
hash fridg: 
	{
	'Ardo-Young'     => [ one | two | three ]
	'Ardor' 	 => [ one | two | three ]
	}
hash wei: 
	{
	'Tefal' 	 => [ one | two | three ]
	}


Доступ к определенному элементу массива можно получить, указывая явно набор ключей ${$hash{$a}{$key}}[$n], где $n < $#{$hash{$a}{$key}}

Примеры

Шпионская программа

Допустим в Пентагоне нужно ввести авторизованный доступ к данным по определенным правилам:

есть полный файл(data.crypt) всех записей вида(расшифрованный)

London#явка1#доступ#прочее#логин1
Damask#явка2#доступ#прочее#логин2
Peru#явка3#доступ#прочее#логин3
И есть файл скажем для агента agent007.crypt, в котором описан вид доступа только к определенным данным:
логин1
логин3
т.е. из файла data.crypt нужно выбрать совпадения строк с подстроками файла agent007.crypt и выбрать из data.crypt все параметры спецзадания(-ий). Результат нужно вывести в виде:
<A NAME=\"a\">
aasd
abf
Absc
afgh
<A NAME=\"b\">
bcd
bgh
bfe
и т.д.

т.е. в отсортированном виде. Шпионы тоже люди. Скрипт ниже решает данную задачу при помощи операций с массивами и использованием хеша массивов(хотя наверное можно было сделать и проще, здесь же главное показать использование хеша массивов):
#!/usr/bin/perl -wT #ключ -T означает повышенную безопасность,
Пентагон все-таки
use strict;
my(@data, @new, $line, $m, @res, @sort, $k, %ha);
my($autor, $pesniya, $position, $u, $n, $im);
open F, "<data.crypt" or die "Err: $!"; @data=<F>; close F;
open F, "<agent007.crypt" or die "Err: $!" @data=<F>; close F;
foreach $line(@new){
  foreach $m (@data){push @res, "$m" if($m=~m/^(.*)#$line$/)}
}
  @sort=map{$_ -> [1]}
        sort{$a->[0] cmp $b->[0]}
        map{[/^(.*)#/, $_]}
        grep{!$_{$_}++} @res;  
foreach $u('a' .. 'z'){
  foreach $n(@sort){push @{$ha{$u}}, $n if($n=~m/^$u/)}
}
for $k(sort keys %ha){print "<a href=\"#$k\">$k</a> "}
print "\n<p><center>\n";
for $k(sort keys %ha){
  print "<a name=\"$k\"> </a><br>\n";
  foreach $im(@{$ha{$k}}){
    ($autor, $pesniya, $position)=split /#/, $im;
    print "$autor, $pesniya, $position<br>\n";
  }
}
print "</center>";


Разберем работу программы:
foreach $line(@new){
  foreach $m (@data){push @res, "$m" if($m=~m/^(.*)#$line$/)}
}

Отсеять из файла data.crypt разрешенные данные для agent007.crypt при помощи сравнения подстрок.

@sort=map{$_ -> [1]}
        sort{$a->[0] cmp $b->[0]}
        map{[/^(.*)#/, $_]}
        grep{!$_{$_}++} @res;  

Отсортировать данные для данного агента в алфавитном порядке по первой ячейке из общей таблицы data.crypt и убрать повторения одинаковых строк.

foreach $u('a' .. 'z'){
  foreach $n(@sort){push @{$ha{$u}}, $n if($n=~m/^$u/)}
}

Создавть хеш массивов, где ключем будет буква, а значением будет массив из строк спецзаданий.
for $k(sort keys %ha){print "<a
href=\"#$k\">$k</a> "}
Вывести линейку начальных букв, по которым будут сортироваться результаты.
for $k(sort keys %ha){
  print "<a name=\"$k\"> </a><br>\n";
  foreach $im(@{$ha{$k}}){
    ($autor, $pesniya, $position)=split /#/, $im;
    print "$autor, $pesniya, $position<br>\n";
  }
}

Вывести массивы, ассоциированные со значениями ключей букв. @{$ha{$k}} - просто обычный массив, доступ к которому зависит от значения ключа $k. Каждый элемент массива стостоит из строки с разделителями #, по нему и разделяет функция split ($autor, $pesniya, $position)=split /#/, $im;

Программа вывода номеров журнала

Еще один пример использования хеша массивов для вывода содержания журнала за несколько лет календарем. Есть директория с файлами содержания журнала по номерам вида:

1.1996.txt
2.1996.txt
3.1996.txt
4.1996.txt
5.1996.txt
...
и т.д. до 
5.2001.txt

где первая цифра в названии файла это содержание журнала за данный месяц, а вторая это год. Читаем эту директорию в массив и вызываем подпрограмму:

while(<$dir/journal/*.txt>){push(@files, $_)}
&calendar;

sub calendar{
  print qq~<center><font><b>Содержание 
         по номерам</b></font><p></center>~;
  @year1=grep{!$test{$_}++ if(/^(\d+)$/)} #выделяет число лет, за которые
есть номера журнала
                                          #и заодно удаляет одинаковыен
года, т.к. 
                                          #на каждый год приходится не
больше 12 
                                          #файлов: 1.1996, 2.1996, 3.1996...
         map{/\.(\d+)/, $_} @files; #выделяет года и заносит их во временный
массив
  foreach $line(@year1){ #цикл по годам.
    foreach $files(@files){
      push @{$numbers{$line}}, $files if($files=~m/$line/);
      #здесь производится заполнение хеша массивов ключами, 
      #которые являются годами, а значениями хешей буду являться
      #массивы номеров журнала за данный год, который является ключом.
      #т.е. в результате должно получиться что-то вида:
      #%hash = (
      #       1996 => ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
"11", "12"], 
      #       1997 => ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10",
"11", "12"], 
      #           ...
      #       2001 => ["1", "2", "3", "4", "5"] #до пятого номера потому,
что 
                                                #шестой номер на момент
написания этого
                                                #примера еще не вышел.
      #);
    }
  }
  print "<center><table>";  #открываем табличку для вывода
результатов
  for $key (sort keys %numbers){#цикл по отсортированным в порядке
возрастания годам
    print "<tr><td><font size=\"1\"><b>$key:
</b></font>"; #печатаем год
    foreach $elem(@{$numbers{$key}}){ #вытаскиваем массив номеров журнала из
хеша, ключем
                                      #которому должен являться определенный
год
      if($elem=~m/\/(\d+).(\d+)\.txt/){$nj=$1; $yj=$2;
        if($nj eq $nomer && $yj eq $year){#текущий номер для
просмотра выделяем красным:
          $temp1=qq~<font size="1" color=red><b>$nj</a>
</b></font>\n~;
          push(@results123, $temp);
        }
        else{#остальные выделяем ссылкой
          $temp2=qq~<font size="1"><b><a 
		            href="$url?month=$nj&year=$yj"
class="menu">$nj</a> 
                    </b></font>\n~;
          push(@results123, $temp2)
        }
      }
    }
    #дальнейшая конструкция называется преобразованием Рэндела 
    #Шварца, смысл которой заключается
    #в том, чтобы отсортировать массив номеров журнала по возрастанию,
    #т.к. при извлечении из хеша они будут выстраиваться в порядке
    #1,11,12,2,3,4...
    @sort123= map{ $_ -> [1]}
              sort{$a->[0] <=> $b->[0]}
              map{[/>(\d+)<\/a>/, $_]} @results123;
    print @sort123; #печатаем табличку номеров журнала за 1995 год
    print "</td></tr>";#закрываем строку таблички
    #обнуляем временные массивы
    $#results123=-1;
    $#sort123-1;
    #возвращаемся наверх и начинаем печатать строчку 
    #таблицы для следующих номеров следующего года.
  }
  print qq~</table></center>~;
}

Все вышеописанное выглядит в виде html примерно так:

Содержание по номерам

1995: 1
1996: 1 2 3 4 5 6 7 8 9 10 11 12
1997: 1 2 3 4 5 6 7 8 9 10 11 12
1998: 1 2 3 4 5 6 7 8 9 10 11 12
1999: 1 2 3 4 5 6 7 8 9 10 11 12
2000: 1 2 3 4 5 6 7 8 9 10 11 12
2001: 1 2 3 4 5




Партнёры:
PostgresPro
Inferno Solutions
Hosting by Hoster.ru
Хостинг:

Закладки на сайте
Проследить за страницей
Created 1996-2024 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру