哈希

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、哈希函数

1keysvalues函数

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";}

只要哈希中有一个键值对,就会返回真。

2each函数

如果需要罗列哈希的每个键值对,常见的写法就是使用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";}

3exists函数

检查哈希中是否有某个键,可以使用该函数,它能返回真或假,表示键存在与否,和键对应的值无关

if(exists $books{"dino"}){
    print "hey, there's a library card for dino!\n.\n";}

4delete函数

从哈希中删除指定的键及其对应的值。加入没有这样的键,它就会直接结束,而不会出现任何警告或错误信息。

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";

posted on 2012-03-17 15:35  半面人  阅读(1161)  评论(0)    收藏  举报

导航