哈希
1、介绍
所谓哈希就是一种数据结构,和数组的相同之处在于:可以容纳很多值(没有上限),并能够随机存取。而区别在于:不像数组是以数字来检索,哈希是以名字来检索。也就是说检索用的键不是数字,而是保证唯一的字符串。
哈希里只有键值对。这些键和值都是任意的标量,但是键总是会被转换成字符串。假如以数字表达式50/20,那么它会被转换成一个含有三个字符的字符串“2.5”。
哈希可能是任意大小,从没有任何键值对的空哈希到填满内存的巨大哈系都可以。
哈希中键是唯一的,但是它们对应的值可以重复。哈希的值可以是数字,字符串,undef,或是这些类型的组合。哈希是从键到值的单行道,我们无法在哈希中查询值并反推出键。
创建哈希的根源是需要将一组数据对应到另一组数据。下面是哈希的应用:只要问题中带有找出重复、唯一、交叉引用、插表之类的字眼,就很可能会用到哈希:
a、按名字找姓:名字可以作为键,而姓可以成为值,当然名字必须唯一
b、用主机名找IP地址
c、按IP地址来找主机名
d、按单词统计出现次数:将要找的单词作为键,每找到一个该单词其值加1。
e、按用户名统计每个人使用的磁盘块数量
f、按驾驶执照号码找出姓名
哈希的典型应用:图书馆记录每个人借出了几本书
$books{"fred"} = 3; $books{"wilma"} = 1;
要判断某项哈希元素的真假很简单
if($books{$someone}){ print "$someone has at least one book checkedout.\n";}
然而哈希里有些元素并不为真
$books{"barney"} = 0; #现在没有借阅图书 $books{"pebbles"} = undef; #从未借阅过图书,这是张新办的借书卡
因为Pebbles不曾结果任何数,所以他的键值是undef,而不是0。
每个键都有相应的值,这个值若不是借出的图书的数量,就是undef,意味着一个从未使用过借书证的读者。
2、访问哈希元素
要访问哈希元素,需要使用如下语法:$hash{$some_key}这和访问数组的做法类似,只是使用了话括号而非方括号来引出索引。
$family_name{"fred"} = "flintstone"; $family_name{"barney"} = "rubble";
为哈希键赋值后,可以写出这样使用
foreach $person(qw<barney fred>){ print "I've heard of $person $family_name{$person}.\n";}
哈希元素会因赋值而诞生:
$family_name{"wilma"} = "flintstone"; $family_name{"betty"} .= $family_name{"barney"};
访问哈希表里不存在的值会得到undef
3、访问整个哈希
要访问整个哈希,可以用%作为前缀。因此前面我们使用的哈希应该称为%family_name。
为了方便起见,哈希可以被转换成列表,反过来也行。对哈希赋值会带来列表赋值的上下文,列表的元素应该是键/值对。
%some_hash = {"foo",35,"bar",12.4,2.5,"hello","wilma",1.72e30,"betty","bye\n"};
在列表上下文中,哈希会自动变成一些简单的键值对:
@any_array = %some_hash;
我们把这个变换叫做哈希松绑,将它鞭策很难个键值对的列表。当然键值对也不一定按照当初赋值的顺序松绑。
选择使用哈希的场合,要么元素存储顺序无关紧要,要么可以容易的在元素输出时进行排序。
哈希赋值:%new_hash = %old_hash;
其实这一行代码会导致%old_hash松绑成为键值对,然后通过类表赋值重新构造每个键值对,最终形成哈希%new_hash。
%enverse_hash = reverse %any_hash;
这样会导致哈希的键值对互换,我们就能够通过值来找键了。这种技巧只在哈希值唯一的情况下才能奏效,否则就会导致重复的键,而这对于哈希是不可能的。对与这个问题Perl采用后发先至的原则,用列表最后的键覆盖之前的键。
命名哈希的另一种方式
my %last_name = ("fred" => "flintstone","dino" => undef,"barney" => "rubble","betty" => "rubble",);
注意,列表结尾有一个额外的逗号,这样写不但无伤大雅,而且也便于维护。
4、哈希函数
1)keys和values函数
keys函数能返回哈希的键列表,而values函数能返回值列表。如果哈希没有任何成员,则两个函数返回空列表。
my %hash = ("a" => 1,"b" => 2,"c" => 3,); my @k = keys %hash; #包含a b c my @v = values %hash; #包办1 2 3
但数组中元素的顺序是无法预测的。
在标量上下文中,这两个函数都会返回哈希中键值对的个数。
my $count = keys $hash #得到3,也就是有3对键值
也可以用来判断真假
if(%hash){ print "That was a true value!\n";}
只要哈希中有一个键值对,就会返回真。
2)each函数
如果需要罗列哈希的每个键值对,常见的写法就是使用each函数,它能用两个元素的列表形式返回键值对。实际使用中,唯一适合使用each的地方就是在while循环中。
while(($key,$value) = each %hash){ print "$key => $value\n";}
while循环的条件中,返回的是列表的的成员数量。当到最后哈希里的值全部返回完后,会返回一个空列表,此情况下,列表元素的个数是0,所以while循环就结束了。
如果输出时希望对哈希进行排序,可以这样
foreach $key(sort keys %hash){ $value = $hash{$key}; print "$key = > $value\n";
或者,可以除去额外的$value变量:
print "$key => $hash{$key}\n";}
3)exists函数
检查哈希中是否有某个键,可以使用该函数,它能返回真或假,表示键存在与否,和键对应的值无关
if(exists $books{"dino"}){ print "hey, there's a library card for dino!\n.\n";}
4)delete函数
从哈希中删除指定的键及其对应的值。加入没有这样的键,它就会直接结束,而不会出现任何警告或错误信息。
my $person = "betty"; delete $books{$person};
这里并不是将undef存入哈希值。在delete之后,键便不会出现在哈希之中,但是存入undef之后键却一定会存在。
5、哈希值内插
可以将单一哈希元素内插到双引号引起的字符串中:
foreach $person(sort keys %books){ if($books{person}){ printf "$person has $books{person} items\n";}
但是整个哈希的内插却是不支持的,"%books"的结果只是6个字符的串%books,现在在双引号中只有一下几个字符需要转义:$、@、"、\四个。
%ENV哈希:Perl程序既然运行在某个环境中,就需要对周围的影响有所感知。Perl获取这些信息的是存取%ENV哈希。例如常常需要从%ENV中存取PATH的键的值
print "PATH is $ENV{PATH]\n";