Chinaunix首页 | 论坛 | 博客
  • 博客访问: 570279
  • 博文数量: 201
  • 博客积分: 7734
  • 博客等级: 少将
  • 技术积分: 1994
  • 用 户 组: 普通用户
  • 注册时间: 2010-04-09 19:18
文章分类

全部博文(201)

文章存档

2011年(28)

2010年(173)

分类:

2010-08-30 17:20:57

第六章:
什么是哈希?
哈希是一种数据结构,和数组的相同之处在于:可以容纳很多值(没有上限),并能随机存取。而区别在于:不像数组是以数字来检索,哈希是以名字来检索。也就是说检索用的键不是数字,而是保证唯一的字符串。
哈希值得一提的是:虽然这些键是唯一的,但是它们对应的值可以重复。哈希的值可以是数字、字符串、undef,或是这些类型的组合。但是哈希键则全都必须是唯一的字符串。
为什么使用哈希?
可以将一组数据对应到另一组数据。
按名字找姓:
名字可以作为键,而姓可以成为值。这当然需要限定名字是唯一的,如果出现了两个叫做randal的人就行不通了。通过哈希可以按任何人的名字找到相应的姓。例如以tom为值取得值phoenix。
用主机名找IP地址
你也许知道在因特网上,每台计算机同时拥有一个主机名如
,以及一个IP地址如123.45.67.89。这因为机器喜欢和数字打交通,但是一般人比较记得名字。主机名是唯一的字符串,可以用来作为哈希的键。通过哈希可以按主机名找到相应的IP地址。
另一种思考方式是将哈希当成极其简单的数据库,其中每个键的“名下”都可以存储相应的数据,事实上只要问题中带有找出重复、唯一、交叉引用、查表之类的字眼,就很有可能会用到哈希。
 
 

访问哈希元素:
$hash{$some_key}
这和访问数组的做法类似,只是使用了花括号而非方括号来引出索引,而且现的键表达式是字符串,而非数字:
$famil_name{"fred"} = "flintstone";
$famil_name{"barney"} = "rubble";
上面两个也可以整形成下面这个样子:
foreach $person (qw< barney fred >) {
  print "I've heard of $person $family_name{$person}.\n";
}

$foo = "bar";
print $family_name{ $foo . "ney" };     
#打印“rubble"


$family_name{"fred"} = "astaire";    
#组已有的元素赋上新值

$bedrock = $family_name{"fred"};    
#得到"astaire";早先的值已不存在


$family_name{"wilma"} = "flintstone";        
#增加一个新的键值对

$family_name{"betty"} .= $family_name{"barney"};    


访问整个哈希:
要指代整个哈希,可以用百分号(%)作为前缀。因此前面我们使用的哈希应该称为%family_name。
哈希可以被转换成列表,反过来也行。对哈希赋值会带来列表赋值的上下文,列表的元素应该应该是键/值对。

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


在列表上下文中,哈希会自动变成一些简单的键/值对:

 

@any_array = %some_hash;
    
    我们把这个变换叫做哈希松绑,将它变成键/值对的列表。当然键/值对也不一定按照当初赋值的顺序松绑。
    print "@any_array\n";
    
#可能会给出下面的结果:

    
#betty bye (以及一个换行) wilma 1.72e+30 foo 35 2.5 hello bar 12.4


哈希赋值:
%new_hash = %old_hash;
更常见的是以某种方式转换哈希,如建立一个反转哈希:
%inverse_hash = reverse %any_hash;


胖箭头:
在哈希赋值时常常会发现列表中的键/值对并不易区分开来。例如在下面的赋值中,任何人都要逐个扫描列表成员,同时默念着:
键、值,键、值等等
如:

%some_hash = ("foo", 35, "bar", 12.4, 2.5, "hello",
            "wilma", 1.72e30, "betty", "bye\n");
如果用胖箭头的方式就很简单了。
my %last_name = (     
#哈希也可以是词法变量

  "fred" => "flintstone",
  "dino" =>    undef,
  "barney" => "rubble",
  "betty" => "rubble",
  );


#还有一个瘦箭头->,它是用来服务于引用(reference)的,可以参考perlreftut或者Perlfre了解这个高级话题。
#胖箭头左边的任何祼词都能自动引用,因此胖箭头左边的课词不需要引号。另外哈希键所在的花括号也有类似的自动引用能力。
#裸词的定义是字母、数字和下划线的序列,可以用加号或减号开头,但不能用数字开头。

哈希函数就是围绕哈希很多有用的函数。

keys 和 values 函数:
keys函数能返回哈希的皱列表,而values函数能返回值列表。如果哈希没有任何成员,则两个函数都返回空列表。

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

这样@k会包含"a","b"和"c",而@v则会包含1、2和3,当然顺序有所不同,但是可以确定返回的键列表和值列表的顺序是一样的。

my $count = keys %hash;  #得到3,也就是说有三对键值

偶尔也能看到别人的程序里把哈希当成布尔表达式来判断真假。
if (%hash) {
 print "That was a true value!\n";
}

each 函数:
如果需要罗列哈希的每个键/值对,常见的写法就是使用each函数,它能用两个元素的列表形式返回键/值对。
每次对同一个哈希调用此函数,它就会返回下一组键/值对,直到所有的元素都被访问过,也就是再没有任何新的键/值对,些时
each会返回空列表。

实际使用时,唯一适合使用each的地方就是while循环中,如下所示:


 

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

依次处理哈希的方法:
foreach $key (sort keys %hash) {
  $value = $hash{$key};
  print "$key => $value\n";
        
#或者,可以略去额外的$value变量;

        
#print "$key => $hash{$key}\n";

}

哈希的典型应用:
Bedrock图书馆的借阅信息程序以一个哈希记录每个人借出了几本书:
$books{"fred"} = 3;
$books{"wilma"} = 1;
判断某项哈希元素的真假:
if ( $books{$someone}) {
  print "$someone has at least one book checked out.\n";
 }
 
 $books{"barney"} = 0;        
#现在没有借阅图书

 $books{"pebbles"} = undef;
#从未借阅过图书,这是张新办的借书卡

 
 exists函数:
 检查哈希中是否有某个键(也就是某人是否有借书证)可以使用exists函数,它能返回真或假,分别表示键存在与否。和键的值无关:
 
if (exists $books{"dino"}) {
  print "Hey, there's a library card for dino!\n";
  }
  
delete 函数:
delete函数能从哈希中删除指定的键及其相对应的值。假如没有这样的键,它就会直接结束,而不会出现任何警告或错误信息。
my $person = "betty";
delete $books{$person};     
#撤回$person的借书卡


哈希值内插:
可以将单一哈希元素内插到双引号引起的字符串中:
foreach $person (sort keys %books) {            
#按次序访问每位借问者

  if ($books{$person}) {
  print "$person has $books{person} items\n";    
#fred借了3本书

}

%ENV哈希:
Perl程序既然运行在某个环境中,就需要周围的影响有所感知。Perl获取这些信息的方法是存取%ENV哈希。
  print "PATH is $ENV{PATH}\n";
  
习题:
1、
=======================================================
#!/usr/bin/perl -w

%given_for_first = (
                    "fred" => flintstone,
                    "barney" => rubble,
                    "wilma" => flinstone);
print "Input given_name:\n";
$given_name = <STDIN>;

chomp($given_name);            
#这里一定要加,开始我没有加,测试了好久都报:Use of uninitialized value in concatenation (.) or string at hash2 line 9, line 1.


$output = $given_for_first{$given_name} ;
printf "output firstname is $output";
#my @k = keys %given_for_first;

#my @v = values %given_for_first;

========================================================
#!/usr/bin/perl -w

my $last_name = qw{
        fred flintstone
        barney rubble
        wilma flintstone
        };
        print "Please enter a first name:";
        chomp(my $name = <STDIN>);
        print "That's $name $last_name{$name}.\n";
        
=========================================================
2、
#!/usr/bin/perl -w

my(@words, %count, $word);        
#声明变量(可以省略)

chomp(@words = <STDIN>);

foreach $word (@words) {
  $count{$word} += 1;            
#或者$count{$word} = $count{$word} + 1;

  }
  
foreach $word (keys %count) {    
#或者sort keys %count

  print "$word has seen $count{$word} times.\n";
}

==========================================================
3、
#!/usr/bin/perl -w

my $longest = 0;
foreach my $key ( keys %ENV ) {
    my $key_length = length{ $key };
    $longest = $key_length if $key_lenth > $longest;
}

foreach my $key ( sort keys %ENV ) {
    printf "%-${longest)s %s\n", $key, $ENV{$key};
}


阅读(1129) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~