Хэш - ассоциативный массив, т.к. доступ к данным осуществляется при помощи ключа, ассоциированного со значением.
Хэш можно определить несколькими способами:
%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)
);
%hash = (
'шляпа' => 'серая',
'водка' => 'горькая',
'вобла' => 'вкусная');
if(exists($hash{"дождь"})){
print "Элемент найден";
}
else{
print "Элемент не найден";
}
%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
%hash = (
'шляпа' => 'серая',
'водка' => 'горькая',
'вобла' => 'вкусная');
while(($key,$value) = each %hash){
print "$key => $value\n";
};
%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";
}
в двух последних случаях мы интерполируем хэш как список, что не позволяет предсказать или управлять порядком вывода пар "ключ/значение". Данные в последних двух случаях выводятся в виде списка ключей и значений, элементы которого разделяются текущим содержимым переменной $", т.е. не удастся вывести каждую пару значений на новой строке или отделить ключи от значений каким-либо символом. Приведем программу, которая читает файл почтового ящика и выводит количество сообщений от каждого отправителя, отправитель определяется по строке 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";
}
$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").
Для перебора элементов хэша в порядке вставки, т.к. 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 вывод хеша в порядке вставки можно сделать при помощи дописывания числовой информации в хеш. Есть файл 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> [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";
}
>> Есть хеш массив (слово, частота), необходимо вывести в файл
пары
>> (слово,частота) отсортированные по частоте. По ключю - просто, а
вот
>> по значению затрудняюсь. Подскахите как проще сделать?
my %hash=('for'=>1000,'to'=>1500,'in'=>2000,'do'=>500);
foreach(sort {$hash{$a} <=> $hash{$b}} keys %hash) {
print $_,'=',$hash{$_},"\n";
}
Как можно хеш положить в строку? Например проблема:Создаешь хэш и держишь в нем те строчки, которые еще, грубо говоря, не закрыты, а как закроешь - удаляй. Конечно, некий заметный кусок файла в памяти будет присутствовать, но не полностью же.
Что-нибудь типа
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;
}
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 - хэш, в котором хранятся обработчики различных ситуаций, возникающих в 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(
name1 => qwe,
name2 => rty,
name3 => asd);
Значения полей name* можно получать $FORM{'name1'}, $FORM{'name2'} и т.д.
@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 |