Learning Perl
第六章 哈希
哈希是一种数据结构,与数组不同之处在于索引方式,数组以数字来索引,哈希则以名字来索引。
哈希的索引值,称为键,并不是数字,而是任意唯一的字符串。
根据Perl去除不必要限制的原则,哈希可以是任意大小的,就是关联数组(associative array)。
哈希的读写性能很高,值可以是数字,字符串,undef或者组合。但是哈希的键必须全都是字符串。
哈希当作简单的数据库,问题中带有找出重复,唯一,交叉引用,查表之类的字眼,就可以用到哈希。
访问哈希元素
$hash($some_key)
和访问数组类似,只是使用花括号,而非方括号表示索引值。
$animal{'dog'} = "caoqing";
$animal{'cat'} = "xiaohuan";
foreach my $animal (qw \dog cat\) {
print "I have heard of $animal $animal{$animal}.\n";
}
哈希键不一定是字符串或简单标量变量,也可以是任意的表达式。
若对某个已存在的哈希元素赋值,会覆盖之前的值。
访问整个哈希
指代整个哈希,可以用%作为前缀,%animal。
哈希可以被转变为列表,列表中的元素应该为键值对:
%some_hash = ('cao',20,'qing',3,'wang',21);
在列表上下文,哈希就是简单的键值对列表:
@array = %some_hash;
这种变换成为展开哈希。
#use 5.012;
%some_hash = ('cao',20,'qing',3,'wang',21);
@array = %some_hash;
print "@array";
wang 21 cao 20 qing 3
顺序错乱,是因为哈希已经为快速检索对键值对的存储做了排序,使用哈希的场合,要么存储顺序无关紧要,要么容易在输出是进行排序。
哈希赋值
哈希支持赋值运算符,my %new_hash = %old_hash;
%old_hash会展开为键值对列表,然后通过列表赋值重新构建键值对,形成新的哈希。
胖箭头
将列表赋值到哈希是常常发现列表中的键值对不容易区分。
Perl使用胖箭头(=>)来代替逗号。
my %animal = (
'dog' => 'caoqing',
'cat' => 'xiaohhuan',
'pig' => 'xiaobao',
);
使用旁箭头是可以省略键的引号,左边的部分可以被自动引用。
无需引号的字符序列,称为裸字。
允许省略键名义好的地方是花括号中检索指定键名的元素。比如$animal{'dog'}可以简写为$animal{dog}。不加引号已经成为惯例,如果花括号内不是裸字,Perl会将其做为表达式求值,结果作为键名。
哈希函数
keys和values函数
keys函数返回哈希的键列表,values函数返回哈希的值列表。
my %hash = ('a' => 1, 'b' => 2, 'c' => 3,);
my @k = keys %hash;
my @v = values %hash;
print "@k\n";
print "@v";
a b c
1 2 3
Perl会根据需要重新优化排列顺序,以保持高速访问。在标量上下文中,这两个元素会返回哈希中的元素个数。这个过程不需要遍历整个哈希,所以非常高效。
my %hash = ('a' => 1, 'b' => 2, 'c' => 3,);
my @k = keys %hash;
my @v = values %hash;
my $count = @k;
print "$count\t";
my $count2 = @v;
print "$count2";
3 3
哈希也可以当作布尔值来判断真假,至少有一个键值对,就返回真。
each函数
需要迭代整个哈希时,常见就是each函数,包含两个元素的列表形式返回键值对每次对同一个哈希调用此函数,就会返回下一组键值对,直到所有的元素都被访问过。在没有任何键值对时,each函数会返回空列表。
唯一适合使用each的地方就是在while循环里。
my %hash = ('a' => 1, 'b' => 2, 'c' => 3,);
while (($key, $value) = each %hash) {
print "$key => $value\n";
}
c => 3
a => 1
b => 2
each返回键值对是乱序的,keys和values返回哈希的自然顺序,如果一次处理哈希,只需要对键排序。
my %hash = ('a' => 1, 'b' => 2, 'c' => 3,);
foreach $key (sort keys %hash) {
$value = $hash{$key};
print "$key => $value\n";
}
a => 1
b => 2
c => 3
哈希的典型应用
exists函数
若要检查哈希中是否存在某个键,可以使用exists函数,返回真假,分别表示键存在与否。
my %hash = ('a' => 1, 'b' => 2, 'c' => 3,);
if (exists $hash{d}) {
print "i love you";
} else {
print "i hate you";
}
i hate you
delete函数
delete函数可以从哈希删除指定的键及其对应的值。假如键不存在,会直接结束,不出现警告信息或错误信息。
my %hash = ('a' => 1, 'b' => 2, 'c' => 3,);
delete $hash{a};
print keys %hash;
bc
哈希元素的内插
%ENV哈希
Perl程序运行环境的信息,print "PATH is $ENV{PATH}.\n"。
PATH is C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;D:\strawberry\c\bin;D:\strawberry\perl\site\bin;D:\strawberry\perl\bin;d:\Program Files (x86)\SSH Communications Security\SSH Secure Shell.
习题
1. 编程读取用户指定的名字并且汇报相应的姓。
2. 编程读取单词,然后打印列出每个单词出现次数的列表。
3. 编程输出%ENV所有键值对,按照ASCII编码排序。
答案
1.
my %last_name = qw {
cao qing
xiao bao
wang huan
};
print "Enter a first name: ";
chomp(my $name = <STDIN>);
print "That is $name $last_name{$name}.\n";
2.
my (@words, %count, $word);
@words = qw(i love you cao qing. you are the best people on the world!);
foreach $word (@words) {
$count{$word} += 1;
}
foreach $word (keys %count) {
print "$word was seen $count{$word} times.\n";
}
3.
my $longest = 0;
foreach my $key (keys %ENV) {
my $key_length = length($key);
$longest = $key_length if $keylength > $longest;
}
foreach my $key (sort keys %ENV) {
printf "%-${longest}s %s\n", $keys, $ENV{keys};
}
转载于:https://my.oschina.net/u/1449160/blog/201357