

哈希是一种和数组类似的数据结构,可以将值存放到其中或者从中取回值;但和数组不同的是,其索引不是数字,而是名字。也就是说,索引(这里将它叫key)不是数字而是任意的唯一的字符串。 key可以是任意的字符串,可以使用任何的字符串作为key,但它们是唯一的。另一种理解hash的方法是把它看作一堆数据(a barrel of data),每一个数据都有一个相应的标签。可以通过标签访问此标签对应的元素,但其中是没有“第一个”元素的概念的。在数组中的元素从0,1,2 开始编号;但在hash中没有确定的顺序,因此也没有第一个元素,只是一些key/value 对的集合对。
keys和values均为任意的标量,但keys通常转换为字符串。由于Perl的“没有不必要的限制”的设计哲学:hash可以是任意大小,从空hash(没有key/value对),到任何内存允许的大小。keys是唯一的,但values可以重复。hash的value可以是数字,字符串,undef,或者它们的混合,但key是唯一的。通过列表设置Hash:
1)列表中第一个元素为 key,第二个为 value。
%data = ('google', 'google.com', 'freeoa', 'freeoa.net', 'taobao', 'taobao.com');
2)也可以使用 => 符号来设置 key/value:
%data = ('google'=>'google.com', 'freeoa'=>'freeoa.net', 'taobao'=>'taobao.com');
3)以下实例是上面实例的变种,使用 - 来代替引号:
%data = (-google=>'google.com', -freeoa=>'freeoa.net', -taobao=>'taobao.com');
使用这种方式 key 不能出现空格,读取元素方式为:
$val = $data{-google}
可以像数组一样从哈希中提取值,哈希值提取到数组语法格式:@{key1,key2}。
@array = @data{-taobao, -freeoa};
my ($tuesday_temp, $friday_temp) = @weekly_temperature{('tuesday', 'friday')};
哈希最常用函数及用法
1.keys和values
keys函数会返回此hash的所有keys,values函数将返回所有的values。如果hash中没有元素,则此函数将返回空列表。
my %hash = ("a"=>1, "b"=>2, "c"=>3);
print my @k = keys %hash;
print my @v = values %hash;
2.each
如果想迭代hash的每一个元素,一种通常的方法是使用each函数,它将返回key/value对应的2个元素列表。
当对同一个hash 函数进行一次迭代时,将返回下一个key/value对,直到所有的元素均被访问。如果没有更多的key/value对,则each函数将返回空表。
my %hash = ("a"=>1, "b"=>2, "c"=>3);
while(($key, $value) = each %hash){
print "$key => $value\n";
}
当然,each返回的key/vlaue对,顺序是混乱的(它其顺序和keys和values函数返回的顺序相同)。如果想将其按序排放,可以对它们排序(使用sort)。
my %hash = ("a"=>1, "b"=>2, "c"=>3, "d"=>4);
foreach $key (sort keys %hash){
$value =$hash{$key};
print "$key => $value\n";
}
哈希的通常用法
1.exists
要查看hash中是否存在某个key,可以使用exists函数,如果hash中存在此key,则返回true,与是否有对应的value无关。
my %hash = ("a"=>1, "b"=>2, "c"=>3, "d"=>4);
if(exists $hash{'a'}){
print "true";
}
2.delete
delete函数将某个给定的key(包括其对应的value)从hash中删除。如果不存在这个key,则什么也不做,不会有警告或者错误信息。其仅指出hash表是否存在某个键;要真正确定是否定义了哈希表中的某个键下面的值,用defind。
my %hash = ("a"=>1, "b"=>2, "c"=>3, "d"=>4);
delete $hash{'a'};
foreach $key (sort keys %hash){
$value =$hash{$key};
print "$key => $value\n";
}
3.hash元素的内插
你可以在双引号的字符串中使用单个hash元素,但不支持整个hash 的内插。
my %hash = ("a"=>1, "b"=>2, "c"=>3, "d"=>4);
foreach $key (sort keys %hash){
print "$key => $hash{$key}\n";
}
定义哈希
首先,什么是哈希?从本质上来说,它是以字符串索引的数组。也就是说,要使用字符串标签而不用数字来存取各个元素。下面是一个例子:
列表A
%alphabet = ('a' => 'apple',
'b' => 'ball', 'c' => 'cat', 'x' => 'xylophone');
上面的几行用四对键—值关联建立了一组Perl 哈希。可以注意到,变量名前面的符号%表示它是一个哈希,箭头用来指出键—值关系。
也可以这样写代码:
列表B
%alphabet = ('a', 'apple', 'b', 'ball', 'c', 'cat', 'x', 'xylophone');
在定义了一个哈希以后,你就可以用定义后的名称对独立的要素进行存取。例如,看下面的代码:
列表C
%alphabet = ('a', 'apple', 'b', 'ball', 'c', 'cat', 'x', 'xylophone');
# access hash value
print "A is for " . $alphabet;
输出结果为:
A is for apple.
要清除哈希,只需简单地把它赋值于一个空的数据集,就像下面的例子:
列表D
# (re)initialize hash
%alphabet = ();
插入、改变和删除哈希元素
可以在一个哈希中插入一个新元素(或者更改一个现有的哈希),只需要给与之相对应的键设置一个新的数值就行了。如果这个键不存在,它将会被创建。下面是一个例子:
列表E
%alphabet = ('a', 'apple', 'b', 'ball', 'c', 'cat', 'x', 'xylophone');
# add new element
$alphabet = 'dog';
# change existing element
$alphabet = 'arrow';
# access hash value
print "A is for " . $alphabet;
你可以用delete()函数删除一对键—值对,就像下面这样:
列表F
%alphabet = ('a', 'apple', 'b', 'ball', 'c', 'cat', 'x', 'xylophone');
# delete element
delete $alphabet;
检索键和数值
如果你觉得一个哈希的键和数值有用,需要进一步处理,Perl允许将其提取到单独的数据结构中。你可以用函数keys()将一个哈希的键返回成为一个数字索引的数组。下面是一个例子:
列表G
%alphabet = ('a' => 'apple', 'b' => 'bat', 'c' => 'cat');
# get and print hash keys
@keys = keys(%alphabet);
print "@keys ";
或者可以用函数values()获得一个具有哈希值的数组,如下:
列表H
%alphabet = ('a' => 'apple', 'b' => 'bat', 'c' => 'cat');
# get and print hash values
@vals = values(%alphabet);
print "@v ";
计算哈希的大小
最简单方法就是用上述的keys()函数,将哈希中的键提取到一个数组中,然后检索得到数组的大小,操作方法如下:
列表I
%alphabet = ('a' => 'apple', 'b' => 'bat', 'c' => 'cat');
# print number of hash elements
print "The hash has " . scalar(keys(%alphabet)) . " elements ";
处理哈希元素
运用while()循环处理一个哈希中的所有元素也比较容易;下面是一个简单的例子:
列表J
%alphabet = ('a', 'apple', 'b', 'ball', 'c', 'cat', 'x', 'xylophone');
# loop over hash
while (($key, $value) = each(%alphabet) ) {
print "$key is for $value ";
}
或者用前面已经讨论过的for()循环和keys()函数。
列表K
%alphabet = ('a', 'apple', 'b', 'ball', 'c', 'cat', 'x', 'xylophone');
# loop over hash
for $k (keys(%alphabet)) {
print $k . " is for " . $hash . " ";
}
运用嵌套的哈希
Perl 也允许在一个哈希(或数组)中再嵌入一个哈希(或数组)。这样就为构建长而复杂的数据结构提供了很大的灵活性,下面是一个例子:
列表L
%movies = (
'black' => {'hero' => 'Batman', 'villain' => 'The Penguin'},
'red' => [{'hero' => 'Spiderman', 'villain' => 'Green Goblin'},
{'hero' => 'Superman', 'villain' => 'LexLuthor'}]
);
# retrieve and print values
print $movies . " fights " . $movies . " ";
print $movies[1] . " fights " . $movies[1] . " ";
该代码返回的结果是:
Batman fights The Penguin
Superman fights LexLuthor
这些基本上就是所有你开始了解Perl中的哈希应该知道的。与数组相关的处理示例如下:
my %dramatis_personae = (
humans => [ 'hamnet', 'shakespeare', 'robyn', ],
faeries => [ 'oberon', 'titania', 'puck', ],
other => [ 'morpheus, lord of dreams' ],
);
Access it like this:
foreach my $group (keys %dramatis_personae) { print "The members of $group are\n"; foreach (@{$dramatis_personae{$group}}) { print "\t$_\n"; } }
---------------------------
push(@{$hash{$key}}, $insert_val);
---------------------------
my $array = []; #create new anonymous array ref
push (@$array, '11');
push (@$array, '12');
push (@$array, '13');
$hash{'first'} = $array; # add
$array = []; #create a new anon-array
push (@$array, '21');
push (@$array, '22');
push (@$array, '23');
$hash{'second'} = $array; # add
print "Hash content\n";
foreach $k (keys %hash) {
foreach (@{$hash{$k}}) {
print " : $_";
}
print "\n";
}
关联数组的一些总结
关联数组和前面说的数组类似,它包含标量数据,可用索引值来单独选择这些数据,和数组不同的是,关联数组的索引值不是非负的整数而是任意的标量,这些标量称为Keys,可以在以后用于检索数组中的数值。关联数组的元素没有特定的顺序,你可以把它们想象为一组卡片,每张卡片上半部分是索引而下半部分是数值。
关联数组是Perl语言中特有的,关联数组是一个功能强大的数组。使用关联数组时要在前面加上%号,关联数组的格式如:
%ARRAY=(key1,value1,key2,value2,key3,value3); 每一个key都有一个相对应的值(value)。
和数组类似,$zmd,@zmd,%zmd 之间没有任何联系,Perl 为对象的不同类型保留独立的命名空间。
下面介绍关联数组的操作:在关联数组中增加、更改一对数据:ARRAY={key}=value;
在关联数组ARRAY中加上一对key-value,要在关联数组名称前加上 $号,而且key 的名称要在 {} 符号之间,最后再指定key所对应的 value 值。如果在这个关联数组中已经有这个key 了,就会更改这个key 所对应的 value。
keys(%array) 操作符可生成由关联数组%array中的所有当前关键字组成的列表,即返回奇数个元素(第1,3,5,7...个)的列表。values(%array) 操作符返回由关联数组%array中所有当前值组成的列表,即返回偶数个列表。
each(%array) 操作符返回由一个关键字和一个值对构成的两个元素的表,对同一数组再操作时返回下一对值直至结束,若没有更多的对时,each() 返回空表(这在打印全部列表中很有用) 。
删除一对在关联数组中的数据:delete $ARRAY{key};
delete是Perl所提供的函数,作用是删除关联数组中的一个key以及这个key 所对应的value。使用方法是在delete 函数之后,指定关联数组中要删除的 key名称。
delete $fred{one}---#将关联数组中key 值为one 的一对key-value 删除,%name=()---#把 %name 关联数组快速置空。
哈希切片(从hash中提取一些key的value来组成新的数组)
my %alphabet = ('a', 'apple', 'b', 'ball', 'c', 'cat', 'f', 'freeoa');
my @sas=@alphabet{'b','f'};
print "@sas";
------------------------------------
Hash Functions
%hash = ( ); Defines an empty hash
%hash = (a => 1, b=>2); Defines a hash with values
$hash{$key} The value referred to by this $key
$hash{$key} = $value; Sets the value referred to by $key
exists $hash{$key} True if the key/value pair exists
delete $hash{$key} Deletes the key/value pair specified by $key
keys %hash Returns a list of the hash keys
values %hash Returns a list of the hash values
------------------------------------
Perl常规与Hash数组相关应用
内置的数组函数有:each, keys, pop, push, shift, splice, unshift, values
内置的hash函数有:delete, each, exists, keys, values
有些是重复且通用的,所以放在一起解释。
数组相关函数:
push:将单元素或一个列表追加到数组的尾部,返回追加后的数组长度
pop:删除数组中的最后一个元素,返回被pop掉的元素
unshift:将单元素或一个列表追加到数组的首部,返回追加后的数组长度
shift:删除数组中的第一个元素,返回被shift掉的元素
splice:从指定位置处删除、插入元素
hash相关函数:(比较简单,所以不介绍)
delete:删除给定key
exists:测试key是否存在于hash中
共同函数:(比较简单,所以不介绍)
keys:获取hash或数组(5.12版本才开始提供)的key,对于数组则是返回索引位置。对于hash返回的key的顺序是不确定的
values:返回hash或数组(5.12版本才开始提供)的值。values返回的元素顺序和keys返回的顺序是一致的
each:遍历hash或数组
push & unshift
将一个列表追加到一个数组的尾部,返回追加后数组的长度:
push ARRAY,LIST
unshift ARRAY,LIST
1.push是将一个列表(可能只有一个元素)追加到数组的最尾部
2.unshift则是将一个列表(可能只有一个元素)追加到数组的最前面
3.它们返回追加后数组的长度
4.追加列表的时候,不是每次追加一个元素,而是一次追加一整个列表,所以新追加到数组中的元素顺序保持不变
注意,unshift在数组开头追加一个列表,会导致数组中原有的元素索引整体后移。例如原数组内容为a b c d,unshift一个新元素后,a就变成第二个元素,索引位置从0变成1,b的索引位置从1变成2,c的索引位置从2变成3,d的索引位置从3变成4。在数组较小时,unshift并不会有什么影响,但数组较大,会严重影响效率。例如:
@arr=qw(python shell php);
push @arr,"perl","Ruby";
print "@arr"; # 输出:python shell php perl Ruby
push的返回值为追加元素成功后,数组的长度:
@arr=qw(python shell php);
print push @arr,"perl","Ruby"; # 输出:5
pop操作,在结果上等价于:
for my $value (@LIST){
$ARRAY[++$#ARRAY] = $value;
}
但push效率更高,因为它是一次追加一整个列表,而非一次追加一个列表中的元素。
pop & shift
pop ARRAY
pop
shift ARRAY
shift
1.pop从数组中移除并返回最后一个元素
2.shift从数组中移除并返回第一个元素
3.如果数组已空,则pop/shift返回undef
4.如果省略ARRAY,则pop/shift操作的是@ARGV,但如果是在子程序中,则操作的是@_
注意,shift删除第一个元素会导致数组的索引位置整体向前移动。例如数组内容为a b c d,但shift一次后,b就变成了第一个元素,它的索引位置从1变成0,c的索引位置从2变成1,d的索引位置从3变成2。在数组较小时,shift并不会有什么影响,但数组较大则会严重影响效率。例如:循环删除数组的最后一个元素:
@arr=qw(python shell php);
while(my $poped = pop @arr){
print $poped,"\n"; # 依次输出:php shell python
}
当省略ARRAY时,且不在子程序中时,pop将操作@ARGV。
while(my $poped=pop){
print $poped,"\n";
}
执行它:
$ ./test.pl fa.txt fb.txt fc.txt
fc.txt
fb.txt
fa.txt
同理在子程序中,则操作默认的参数数组@_。
sub mysub(){
while(my $poped=pop){
print $poped,"\n";
}
}
&mysub(qw(ta.txt tb.txt tc.txt)); # 将依次输出tc.txt tb.txt ta.txt
splice
pop/push、unshift/shift操作的都是数组的开头,或者末尾。splice(中文译为:粘接)则可以指定操作数组中的哪个位置。
splice ARRAY
splice ARRAY,OFFSET
splice ARRAY,OFFSET,LENGTH
splice ARRAY,OFFSET,LENGTH,LIST
splice在移除元素时,在列表上下文返回被移除的元素列表,标量上下文返回最后一个被移除的元素。
1.一个参数时,即splice ARRAY,表示清空ARRAY。
use 5.010;
@arr=qw(perl py php shell);
@new_arr=splice @arr;
say "original arr: @arr"; # 输出:空
say "new arr: @new_arr"; # 输出原列表内容
如果splice在标量上下文,则返回最后一个被移除的元素:
use 5.010;
@arr=qw(perl py php shell);
$new_arr=splice @arr;
say "$new_arr"; # 输出:shell
2.两个参数时,即splice ARRAY,OFFSET,表示从OFFSET处开始删除元素直到结尾。
注意,OFFSET可以是负数。例如:
use 5.010;
@arr=qw(perl py php shell);
@new_arr=splice @arr,2;
say "original arr: @arr"; # 输出:perl py
say "new arr: @new_arr"; # 输出:php shell
如果offset为负数,则表示从后向前数第几个元素,-1表示最后一个元素。
@arr=qw(perl py php shell);
@new_arr=splice @arr,-3;
say "original arr: @arr"; # 输出:perl
say "new arr: @new_arr"; # 输出:py php shell
3.三个参数时,即splice ARRAY,OFFSET,LENGTH,表示从OFFSET处开始向后删除LENGTH个元素。
注意,LENGTH可以为负数,也可以为0,它们都有奇效。例如:
use 5.010;
@arr=qw(perl py php shell ruby);
@new_arr=splice @arr,2,2;
say "original arr: @arr"; # 输出:perl py ruby
say "new arr: @new_arr"; # 输出:php shell
如果length为负数(假设为-3),则表示从offset处开始删除,直到尾部还保留-length个元素(-3时,即表示尾部保留3个元素)。例如:
use 5.010;
@arr=qw(perl py php shell ruby java c cpp js);
@new_arr=splice @arr,2,-2; # 从php开始删除,最后只保留cpp和js两个元素
say "orig arr: @arr"; # 输出:perl py cpp js
say "new arr: @new_arr"; # 输出:php shell ruby java c
如果正数length的长度超出了数组边界,则删除结尾。如果负数length超出了边界,也就是保留的数量比要删除的数量还要多,这时保留优先级更高,也就是不会删除。例如,从某个位置开始删除,后面还有2个元素,但如果"length=-2",则这两个元素不会被删除。
如果length为0,则表示不删除,这个在有第4个参数LIST时有用。
4.四个参数时,即splice ARRAY,OFFSET,LENGTH,LIST,表示将LIST插入到删除的位置,也就是替换数组的部分位置连续的元素。例如:
use 5.010;
@arr=qw(perl py php shell ruby);
@list=qw(java c);
@new_arr=splice @arr,2,2,@list;
say "original arr: @arr"; # 输出:perl py java c ruby
say "new arr: @new_arr"; # 输出:php shell
如果想原地插入新元素,而不删除任何元素,可以将length设置为0,它会将新列表插入到offset的位置。
use 5.010;
@arr=qw(perl py php shell ruby);
@list=qw(java c);
@new_arr=splice @arr,2,0,@list;
say "original arr: @arr"; # 输出:perl py java c php shell ruby
say "new arr: @new_arr"; # 输出:空
注意:上面的代码会在php前面插入新元素集。
splice功能非常好用,在不少语言中,要实现类似的功能,需要借助链表的方式来实现,比较复杂。splice函数即可轻松实现,但链表毕竟增、删元素的效率高,所以对于大数组又要频繁增删改的时候,还是是现成链表比较好。