哈希是一种数据结构,和数组类似,可以将值存放到其中,或者从中取回值。但是,和数组不同的是,其索引不是数字,而是名字。也就是说,索引(这里,我们将它叫key)不是数字而是任意的唯一的字符串。keys 可以是任意的字符串,你可以使用任何的字符串作为key。但,它们是唯一的。

另一种思考hash 的方法是,把它看作一堆数据(a barrel of data),每一个数据都有一个相应的标签。可以通过标签访问此标签对应的元素。但其中是没有“第一个”元素的概念的。在数组中,数组元素从0,1,2 开始编号。但在hash中,没有确定的顺序,因此也没有第一个元素。只是一些key/value 对的集合。hash 可以是任意大小,从空hash(没有key/value 对),到任何你内存允许的大小。keys 是唯一的,但values 可以重复。hash 的value 可以是数字,字符串,undef,或者它们的混合,但key 是唯一的。可以把hash 看作一个简单的数据库,其中每一个key 下面可以有一块数据。要访问hash 元素,可以使用下面的语法:


$hash{$some_key}


这和访问数组元素的方法有些类似,这里下标(key)上使用的是花括号({}),而不是方括号([ ])。现在key 的表达式是字符串,而非数字:可以窥探到LarryWall 的设计思想:Larry 认为由于其和普通数组不同,则也应当使用和普通数组不同的符号。

$family_name{“fred”} = “flintstone”;
$family_name{“barney”} = “rubble”;


hash 的名字和Perl 中其它的标识符的命名规则是一样的(字母,数字,下划线组成,但不能由数字开头)。由于其属于不同的名字空间,则像hash 元素$family_name{“fred”}和子程序&family_name 之间没有任何的关系。Hash 的key 可以是任意的表达式:


$foo = “bar”;
print $family_name{$foo . “ney”}; #输出“rubble”


当将某个值存储在已经存在的hash 元素中,以前的值会被覆盖:

$family_name{“fred”} = “astaire”; #将新值赋给已经存在的元素
$bedrock = $family_name{“fred”}; #得到“astaire”;以前的值丢失了


可以通过赋值语句对hash 元素赋值:


$family_name{“wilma”} = “flintstone”; #新增一个key(也包括value)
$family_name{“betty”} .= $family_name{“barney”}; #创建一个新元素


访问不存在的hash 元素得到undef:


$grantie = $family_name{“larry”}; #没有larry:得到undef


要引用整个hash,使用百分号(“%”)作为前缀。前面几页中使用的hash 的名字为%family_name。为了方便,hash 可以转换为列表,或者反过来。给hash 赋值其类型属于列表context 赋值,其中列表是key/value 对:虽然可以使用任何列表表达式,但其元素个数必须是偶数,因为由key/value 对组成。元素个数为奇数是不可靠的,这通常会引起警告。


%some_hash = (“foo”, 35, “bar”, 12.4, 2.5, “hello”, “wilma”, 1.72e30, “betty”, “bye\n”);


hash 的值(在列表context 中)是一个key/value 对的列表:


@array_array = %some_hash;


这称为:将hash 展开,并将其key/value 对返回到一个列表中。返回的key/value 顺序和存放的顺序可能不同:


print @any_array\n”;

#可能得到如下的结果:betty bye(换行),wilma 1.72e+30 foo 35 2.5 hello bar 12.4


使用如下的语法在hash 之间拷贝:


%new_hash = %old_hash;

这段代码告诉Perl 将%old_hash 展开成key/value 的列表,再将其赋给%new_hash,其将key/value 对一个一个加入的。hash 转变成其它形式更加常见。例如,我们可以将hash 反转:

%inverse_hash = reverse %any_hash;

这会将%any_hash 展开成key/value 对的列表,其列表如(key,value,key,value,key,value,……)。然后, 将其反转,得到(value,key,value,key,value,key,……)。value 和key 的位置交换了。当将其存放在%inverse_hash ,我们就可以查询对于%any_hash 来说是value 的字符串,因为现在它对于%inverse_hash 来讲是key 了。



大箭头符号(=>)。对于Perl 来讲,其作用和和逗号(,)类似,因此有时称作“胖逗号(fat comma)”。Perl 语法中,在需要逗号(,)的时候,都可以使用大箭头符号替换;对于Perl 来讲,它们是一样的。下面是给hash 赋值的另一种方法:小箭头(->)。它和引用一起使用,这是高级话题。如果你准备好了,可以参见perlreftut 和perlref 的帮助手册;它们在技术上还是有一点不同:任何大箭头符号(=>)左侧的bareword(由字母,数字,下划线,但不是由数字开头,前面有可选的加号或减号,组成的序列)都暗含着由引号括起来了的。因此可以省略掉大箭头符号(=>)左侧bareword 上的引号。你也可以忽略掉hash的花括号中的引号,如果里面只有作为key 的bareword.


my %last_name = (
“fred” => “flintstone”,
“dino” => undef,
“barney”=> “rubble”;
“betty”=> “rubble”,
);

注意列表中最后一个逗号


keys 函数会返回此hash 的所有keys,values 函数将返回所有的values。如果hash 中没有元素,则此函数将返回空列表:

my %hash = (“a”=>1, “b”=>2, “c”=>3);
my @k = keys %hash;
my@v = values %hash;

现在,@k 含有“a”, “b”, “c”,而@v 含有1, 2, 3,其顺序可能不同。记住,Perl 并不维护hash 中元素的顺序。但,其中keys按照某种顺序存储,则其对应的values 也是这个顺序:如果“b”排在keys 的最后,则2 也排在values 的最后。如果“c”是第一个,则3 也是第一个。有时,你可能看到有人将hash 作为boolean(true/false)表达式来使用:


if(%hash){
print “That was a true value!\n”;
}

上述语句为true,当且仅当其至少含有一个key/value 对。



如果想迭代hash 的每一个元素(如,检查每一个元素),一种通常的方法是使用each 函数,它将返回key/value 对的2元素列表。当对同一个hash 函数进行一次迭代时,将返回下一个key/value 对,直到所有的元素均被访问。如果没有更多的key/value 对,则each 函数将返回空表。实践中,一般只在while 循环中使用each:


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

上述代码中,首先,each %hash 将从hash 中返回一个key/value 对;假设key 为“c”,value 为3,则列表为(“c”,3)。这个列表赋给($key, $value),现在$key 为“c”,而$value 为3。


由于每一个hash 都有一个私有的迭代器(iterator),因此,使用each 的循环是可以嵌套的,因为不同的hash 有不同的迭代器(iterator)。each 返回的key/vlaue 对,顺序是混乱的(它其顺序和keys 和values 函数返回的顺序相同)。如果想将其按序排放,可以对它们排序(使用sort),大致如下:


foreach $key (sort keys %hash){
$value =$hass{$key};
print $key => $value\n”;
#也可以不使用额外的临时变量$value
#print “$key => $hash{key}\n”;
}


要查看hash 中是否存在某个key(某人是否有借书卡),可以使用exists 函数,如果hash 中存在此key,则返回true,这和是否有对应的value 无关:


if(exists $books{$dino}){
print “Hey, there’s a libaray card for dino!\n”;
}

这即是说,当且仅当hash 中(keys % books)的keys 的列表中存在dino 时,exists $books{“dino”}才返回true。


delete 函数将某个给定的key(包括其对应的value)从hash 中删除。(如果不存在这个key,则什么也不做;不会有警告或者错误信息。)


my $person = “betty”;
delete $books{$person}; #将$person 的借书卡删除掉

这和hash 中存储的为undef 是不同的。使用exists($books{“betty”})将给出相反的结果。使用delete 后,hash 中将不会存在此key;如果其值是udnef,则key 是存在的。可以在双引号的字符串中使用单个hash 元素:

foreach $person (sort keys %books){
if($books{$person}){
print $person has $books{$person} items\n” #fred 有3个
}
}

但不支持整个hash 的内插;“%books”仅是六个不同的字符%books



写一个程序,提示用户输入given name(名),并给出其对应的family name(姓)。使用你知道的人名,或者表6-1(如果你在计算机上花了太多时间,以致什么人都不认识):


#!/usr/bin/perl
my %last_name=qw{fred flintstone
barney rubble
wilma flintstone};
chomp($name=<STDIN>);
print "Enter a first name:$name\n";
if(exists $last_name{$name})
{
print "That is $name $last_name{$name}.\n";
}


写一个程序,读入一串单词(一个单词一行)◆,输出每一个单词出现的次数。(提示:如果某个作为数字使用值是undefined 的,会自动将它转换为0。)如果输入单词为fred, barney, dino, wilma, fred(在不同行中),则输出的fred 将为


#!/usr/bin/perl
my(@words,%count,$word);
@word=qw{fred darney dino barney fred};
foreach $word(@words)
{
$count{$word}+=1;
}
foreach $word(sort(keys %count))
{
print "$word was seen $count{$word} times";
}