操作hash

hash切片

Perl也支持对hash变量的切片。

例如:

my %phone_num = (
  longshuai =>"18012345678",
  xiaofang =>"17012345678",
  tun_er =>"16012345678",
  fairy =>"15012345678"
);
my ($a,$b,$c) = @phone_num{qw(xiaofang fairy xiaofang)};

需要注意的几点是:

  • hash切片使用@前缀,而不是%前缀,因为它代表访问多个内存数据空间
  • hash切片的索引部分是一个列表上下文,是表示键的列表
    • 因此@h{qw(a b)}@h{"a","b"}是有效的,但省略双引号@h{a,b}是错的
  • hash不可内插至双引号,但hash的切片可以内插到双引号,因为hash切片的结果是一个列表
  • hash切片可以作为左值,从而修改hash中对应的键值对数据
my %phone_num = (
  longshuai =>"18012345678",
  xiaofang =>"17012345678",
  tun_er =>"16012345678",
  fairy =>"15012345678"
);

# 双引号中内插hash切片
say "@phone_num{qw(fairy longshuai)}";

# hash切片作为左值
@phone_num{qw(fairy longshuai)} = qw(155555555 188888888);
say "@phone_num{qw(fairy longshuai)}"; # 输出:155555555 188888888

hash类内置函数

perl提供了几个基本的hash内置函数:delete、each、exists、keys、values。

keys和values

keys和values分别用来获取hash的key列表和value列表。注意,hash各元素的出现顺序是不可预测的。

my %h = qw(k1 v1 k2 v2 k3 v3);
my @keys = keys %h;
my @values = values %h;
say "keys: @keys";     # 输出:keys: k3 k2 k1
say "values: @values"; # 输出:values: v3 v2 v1

each和遍历hash

each常和while结合用来遍历数组和hash。每次each迭代时,都获取索引和对应值,并作上位置标记,下次从标记处开始继续迭代。需小心使用each,原因可参考小心使用each

例如:

while(my ($k, $v) = each %hash){
  say "key: $k, v: $v";
}

for、foreach也可以遍历hash,但只能通过keys函数来遍历Key,通过values函数来遍历Value。

my %myhash = qw(k1 v1 k2 v2 k3 v3);

# foreach迭代遍历key
foreach my $k (sort keys %myhash){
 say $k, $myhash{$k};
}

# 迭代遍历value
foreach my $v (values %myhash){
  say $v;
}

此外,还需注意,each、keys、values这三个函数共用一个迭代指针,每次调用keys、values时都会重置each的迭代指针。因此,while + each遍历hash时,循环体内要小心使用keys、values函数。

例如,下面的代码将无限循环。

while(my ($k, $v) = each %p){
  say "k: $k, v: $v";
  keys %p;
  # keys在空上下文中只重置迭代指针而不返回key列表,因此效率不受影响
}

exists和判断键是否存在

exists用来判断hash结构中是否存在某个key,如果存在则返回表示布尔真的结果(数值1)。由于Perl中访问hash结构中不存在的键值对时不会报错,而是返回undef,因此有时候也会直接使用hash索引的方式来测试。但注意,如果key存在于hash结构中,但其对应的值表现为布尔假(即value为undef、0或空字符串等值)时,两种方式测试结果将不同。

my %h;
if(exists $h{k1}){...}
if($h{k1}){...}

$h{k1} = undef;
if(exists $h{k1}){say "1"}  # 输出
if($h{k1}){say "2"}  # 不输出

删除键值对和清空hash

delete用来删除hash结构中的键值对,可以使用hash切片方式一次性删除多个键值对。

my %hash = (
  a => "aa",  b => "bb",
  c => "cc",  d => "dd"
);

delete $hash{a};         # 删除单个键值对
delete @hash{qw(b c)};   # 根据hash切片删除
$, = "-";
say %hash;   # d-dd

作为技巧,delete @HASH{keys %HASH}会清空hash,但效率很低。清空hash效率更高的是下面这两种方式:

%HASH = ();     # 直接清空hash
undef %HASH;    # 注销hash变量

delete还有一个特殊的用法:delete local,它只会在当前作用域内删除hash键值对,退出该作用域后,被删除的键值对仍然存在。

my %hash = (
  a => "aa",  b => "bb",
  c => "cc",  d => "dd"
);

$, = '-';
{
  delete local $hash{a};
  say %hash;   # 输出:b-bb-c-cc-d-dd
}
say %hash;     # 输出:d-dd-c-cc-a-aa-b-bb