查看原文
其他

Perl零零八速成系列---哈希和子函数

晨光 生信者言 2022-03-29

阅读用时:全文共2部分,约4100字,约7分钟

关键词:哈希、子函数、变量操作


上一节Perl编程课程中,我们介绍了许多常用的函数,比如split函数、join函数等,能够轻松地对数组进行操作,那么问题来了,我们能否自己编写类似的功能函数呢?这个可以有!


今天这节课我们就来学习一下哈希和子函数的相关知识。


《Perl零零八速成系列》(共8节课)主要是面向无Perl编程经验或者Perl语言初学者,介绍Perl的基本知识和实用编程技巧。晨光与你一路,轻松点亮编程技能,一跃成为特工级的Perl编程牛人!

【本节内容】

   本节内容包括Perl 语言的哈希和子函数。

 

1

哈希

 

上一节课介绍了Perl语言的数据结构和变量基本操作,讲到哈希(hash)是一个无序的key/value(键/值)对集合,可以使用键作为索引获取相应的值,而且每个键是唯一的(如同一个装有各种东西并贴有标签的桶,只不过哈希装的是数据而已,如下图)。那么这节课我们首先就来学习一下哈希的相关操作。

 

哈希数据桶

 

PS: 笔者曾经表示好奇,为啥非要比喻成桶而不是其它容器,例如箱子或者篮子什么的,后来想想,可能是参考书的原作者比较喜欢用"桶"这个词吧,此时脑海飘来各种桶…

 

哈希的赋值

 

上一节课我们简单地介绍了哈希的赋值,实际上哈希的赋值操作不止一种,而是有多种方式,主要包括:


利用胖箭头赋值

#!/usr/bin/perl

 

%data = ('google' => 'google.com',

'runoob' => 'runoob.com',

'taobao' => 'taobao.com',

);

print "\$data{'google'} =$data{'google'}\n";

print "\$data{'runoob'} =$data{'runoob'}\n";

print "\$data{'taobao'} =$data{'taobao'}\n";

 

其中的胖箭头('=>',与表示引用的瘦箭头'->'区分开)表示键与值的对应关系。


PS: 胖箭头赋值的最大好处就是比较清晰和直观。

 

利用列表赋值

#!/usr/bin/perl

 

%data = ('google', 'google.com', 'runoob','runoob.com', 'taobao', 'taobao.com');

 

列表中每两个元素为一对组合,依次为key和value,这种赋值方式的特点就是,当键值对比较多的时候,键和值就没那么好区分了。


PS: 用列表赋值时,宝宝们需要一个个地数 "键、值、键、值…",当哈希的键比较多时,可能会数着数着就睡着了


利用哈希赋值

 

哈希可以通过一般的赋值语句来进行赋值:

 

my %new_hash =%old_hash;

另外,还可以通过对现有哈希进行转换得到新的哈希,比如建立一个反序的哈希:

 

my %inverse_hash= reverse %any_hash;

 

以上操作会对原有哈希的键-值对进行互换,也就是说原来哈希的值变成了新哈希的键。

 

PS: 这个操作有一个前提,原哈希的值不能有重复,因为这样新哈希的键会出现重复,会导致后面的键-值对覆盖前面的键-值对。

 

哈希函数

 

这里我们介绍一些常用的哈希函数:

 

keys和values函数

 

keys 函数可以返回哈希的键列表,而values 函数能返回对应的值列表:

 

#!/usr/bin/perl

 

%data =('google'=>'google.com', 'runoob'=>'runoob.com','taobao'=>'taobao.com');

 

@k = keys%data;                #@k 包含%data 所有键

@v = values%data;              #@v 包含%data 所有值

 

print"@k\n";

print"@v\n";

 

运行结果:

runoob taobao google

runoob.com taobao.com google.com

 

可以看出,@k和@v 中元素的顺序和哈希赋值时的顺序并不相同,而且每次运行以上代码,可能顺序都会不一样,因为哈希的键是无固定顺序的。

 

PS: 细心的你可能会发现存在一个规律,没错,在每次运行结果中,@k和@v中键和值的顺序是一致的,此时是不是应该在心里给聪明的自己竖起大拇哥呢…

 

exists 函数

 

如果要检查哈希中是否存在某个键,可以使用exists函数来判断,返回真或假:

 

#!/usr/bin/perl

 

%data =('google'=>'google.com', 'runoob'=>'runoob.com','taobao'=>'taobao.com');

 

if(exists($data{'facebook'} ) ){

   print "facebook 的网址为$data{'facebook'} \n";

}

else

{

   print "facebook 键不存在\n";

}

 

运行结果:

facebook 键不存在

 

PS: 在上面的一段代码中,我们使用了if(condition){cmd1}else{cmd2}语句,表示如果()中condition为真,就运行前面{}中的cmd1命令,否则运行后面{}中的cmd2命令。


举个🌰:if(好好学习){考上好大学}else{去工地搬砖}。是不是好理解的多?


defined 函数


与exists 函数不同的是,defined函数是用来判断一个变量是否被赋值,例如:

 

#!/usr/bin/perl

 

my %hash;

$hash{'a'}=undef;                           #键 'a' 对应的值为undef,也就是未赋值

 

if(defined $hash{'a'}){                     #判断 $hash{'a'} 是否被赋值

print "\$hash{'a'} was defined.\n";

}else{

print"\$hash{'a'} was not defined.\n";

}

 

运行结果:

$hash{'a'} was not defined.            #说明$hash{'a'} 没有被赋值

 

PS: 讲到这里是否还有童鞋,对于exists和defined 函数傻傻分不清楚呢?


简而言之,exists 只是判断哈希元素是否存在,无论是否被赋值("有名无分"也行),而defined 则判断元素是否有存在而且被赋值("有名有分"才行)。


delete 函数

 

如果想删除哈希元素,你可以使用delete 函数:

 

#!/usr/bin/perl

 

%data =('google'=>'google.com', 'runoob'=>'runoob.com', 'taobao'=>'taobao.com');

 

@k1 = keys %data;                            #之前的所有键

delete $data{'taobao'};                      #删除键 'taobao'

@k2 = keys %data;                            #之后的所有键

 

print "before: @k1\n";                      

print "after: @k2\n";     

 

运行结果:

 

before: taobao runoob google

after: runoob google

  

each函数

 

如果想遍历整个哈希的所有键,可以用each函数,它每次被调用可以返回包含一个键-值对的列表,用while循环语句实现遍历哈希所有键:

 

#!/usr/bin/perl

 

%data =('google'=>'google.com', 'runoob'=>'runoob.com','taobao'=>'taobao.com');

 

while ( ($key,$value)=each %data ) {

       print " $key => $value\n";

}

 

运行结果:

 

google =>google.com

runoob =>runoob.com

taobao =>taobao.com

 

PS: while 语句中的each函数会遍历哈希的所有键,而且键的顺序是随机的。

 

2

子函数


什么是子函数

 

Perl子函数或子程序(subroutine)也就是用户定义的函数,即执行一个特殊任务的一段分离的代码,它可以使减少重复代码且使程序易读。Perl子函数的名称只能由字母、数字和下划线组成(但不能以数字开头),语法格式如下:


#子函数定义:

sub Hello{                                           #定义子函数名称

  print "Hello, World!\n";                     #子函数主体代码

}

 

#子函数调用:

Hello();                                                #或在函数名前加"&",即&Hello()                            

 

运行结果:

 

Hello, World!

 

子函数的使用

 

上述例子介绍了子函数的基本语法,理解起来比较简单,但是子函数在具体使用过程中,会涉及到参数传递、返回值以及变量的私有性等问题,下面我们就一一介绍。

 

子函数的参数

 

和其他编程语言一样,Perl子函数也可以接受1个或多个参数,子函数参数使用特殊数组 @_ 标明。因此子函数第一个参数为 $_[0], 第二个参数为 $_[1], 以此类推,例如:

 

#!/usr/bin/perl

 

sub Average{                                             # 定义求平均值函数

   $n = scalar(@_);                                      # 获取所有传入参数的个数

   $sum = 0;

 

   foreach $item (@_){                                 # 遍历所有传入的参数

      $sum += $item;                                   # 利用 '+=' 运算符计算数字之和

   }

   $average = $sum / $n;

   print '传入的参数为 :',"@_\n";                 # 打印整个数组

   print "第一个参数值为 :$_[0]\n";             # 打印第一个参数

   print "传入参数平均值为 :$average\n";  # 打印平均值

}

 

Average(10, 20, 30);                                   # 调用函数

 

运行结果:

 

传入的参数为 : 10 20 30

第一个参数值为 : 10

传入参数平均值为 : 20


子函数的返回值

 

子函数可以使用return语句来返回函数值,如果没有使用return语句,则子函数的最后一行语句将作为返回值:

 

#!/usr/bin/perl

 

# 方法定义

sub add {

   $_[0]+$_[1];                   #不使用 return,返回最后一行执行语句的值

   # return $_[0]+$_[1];     # 使用 return,返回指定的值

}

print add (1, 2)

 

运行结果:

3

 

子函数的私有性变量

 

默认情况下,Perl中所有的变量都是全局变量,这就是说变量在程序的任何地方都可以调用。如果我们只希望变量在某个局部被调用,需要设置私有变量,这时可以使用my(表明是特定区域专属的)操作符来设置作用区域。例如:

 

#!/usr/bin/perl

 

$string = "Hello,World!";       #全局变量


sub PrintHello{                                          

   my $string;                         #PrintHello 函数内定义的私有变量

   $string = "Hello, Runoob!";               

   print "函数内字符串:$string\n";            

}

 

PrintHello();                            #调用函数

 

print "函数外字符串:$string\n";

 

运行结果:

 

函数内字符串:Hello, Runoob!

函数外字符串:Hello, World!

 

PS: 通过my 创建的变量,有效区域只限于从声明开始的地方,到闭合作用域的结尾。闭合作用域可以是一对花括号{}中的区域,比如一个 if, while, for, foreach等语句的{}中。

 

 

本文尽量精简地介绍了Perl语言中哈希的基本操作和子函数的使用,并配上简单的示例供大家练习巩固。由于篇幅有限,难以深入展开说明,谨以此文,送与初入门Perl语言的朋友们。

 

 

下一节课,我们来介绍介绍perl语言中最亮眼的一部分:正则匹配,敬请大家关注!

 

参考资料:

《Perl语言入门第六版(中文版)》

http://www.runoob.com/perl/perl-tutorial.html

http://www.runoob.com/try/runcode.phpfilename=HelloWorld&type=perl



 

 

【完】



作者原创作品,未经授权禁止转载!


扫码关注,获取更多精彩内容

关注公众号后:


回复文字:好好学习,收听喜马拉雅FM电台栏目《一分钟听懂NGS基础概念》,让生信分析不再遥不可及。


回复文字:果然科学,给你看一篇好玩的科普文章。

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存