前言
继续今日学习~~
学会使用多维hash是我perl一级菜鸟进化成二级菜鸟的标志事件,目前等第二个标志事件已经等了一年半,还没有等到。因此我觉得还是把多维hash一些小事总结下好了。
正文
赋值
一维hash的赋值方式多种多样,而多维hash我一般就乖乖使用最基本的单点赋值方式,绝对不浪:
my %my_hash;
my $sig1 = "amod_bmod_awsize";
my $sig2 = "bmod_amod_bresp";
$my_hash{$sig1}{name} = "amod_bmod_awsize";
$my_hash{$sig1}{width} = 8;
$my_hash{$sig1}{link}{src} = "amod";
$my_hash{$sig1}{link}{dst} = "bmod";
$my_hash{$sig2}{name} = "bmod_amod_bresp";
$my_hash{$sig2}{width} = 2;
$my_hash{$sig2}{link}{src} = "bmod";
$my_hash{$sig2}{link}{dst} = "amod";
$my_hash{$sig2}{sel}{0} = 0;
$my_hash{$sig2}{sel}{2} = 2;
$my_hash{$sig2}{sel}{10} = 10;
当然了,多维hash一样可以跟一维一样用胖箭头赋值:
my %ascii_for_char = (
'digit' => {
'0' => 48,
'1' => 49,
},
'upper case' => {
'A' => 65,
'B' => 66,
},
'lower case' => {
'a' => 97,
'b' => 98,
},
);
上面这个是我复制过来的,我自己还是喜欢把各个维度展开写,突出一个稳字。
多维hash的某一个key可能指向某一个值,也可能指向一个hash,例如上式中$my_hash{$sig2}指向一个多维hash,$my_hash{$sig2}{name}指向一个值。
而一个key是不能即指向一个值又指向一个其他的东西的,这个道理说起来太简单了,但是我之前就卯足了劲想这么用:
$my_hash{$sig} = "awsize";
$my_hash{$sig}{src} = "a";
$my_hash{$sig}{dst} = "b";
我当时觉得非常合理啊,我这个$sig指向一个信号名,然后下携两个子项分别是src和dst,天衣无缝非常完美的。但是呢,编译时候就在这报错,后来才想明白一个key不能有多个指向。
赋值时,$my_hash{$sig}{dst} = "b" 和 $my_hash{$sig}{"dst"} = "b"是一样的,当然%hash_A = ("key1" => value1, "key2" => value2) 和 %hash_A = (key1 => value1, key2 => value2) 也是一样的,keyx会自动被引号引起来。
打印
一维hash一般直接print %hash就够用了,而对于嵌套的多维数组呢,可以借助一个库来实现清晰的打印:
use Data::Dumper;
my %my_hash;
........
print Dumper(\%my_hash);
打印效果不错,但还是有点美中不足(主要是递进看着难受):
所以本着有轮子还要自己造好看的轮子的原则,死心眼的写了这个函数:
sub display_hash{
my $obj = shift @_;
my $tab_n = shift @_;
my $tab = "";
my @re;
$tab_n = 0 unless $tab_n;
$tab = " " x $tab_n;
my %hash = %$obj;
for my $key(sort keys %hash){
$str = $tab."$key";
if(ref $hash{$key} eq "HASH"){
#$str .= "\n";
push(@re, $str);
@re = (@re, &display_hash(\%{$hash{$key}}, $tab_n+4));
} else {
$str .= " => $hash{$key}";
push(@re, $str);
}
}
return @re;
}
sub display_list{
my @list = @_;
for my $obj (@list){
print "$obj\n";
}
}
这样的话,打印hash就是这个效果:
&display_list(&display_hash(\%my_hash));
看着舒服了很多啊,而且可控性也高了很多,比如在里面加加颜色啥的都很方便;
使用sort时候注意,他的排序是根据首字符的,因此是0 10 2这样的顺序(即使key写成int(10)也不行),如果想通过key索引有先后顺序的value,不妨考虑使用1 11 111 1111这样做key?
数字的排序需要用$a <=> $b的写法,但是在遍历key时候应该怎么使用呢?我还没想清楚。
删除
同一维hash一样,删除一个key会将其所指向的内容全部删除:
delete $my_hash{bmod_amod_bresp}{width};
delete $my_hash{"amod_bmod_awsize"}{name};
&display_list(&display_hash(\%my_hash));
遍历
这样就ok了,下层hash用%{}转一下:
for my $obj0 (sort keys %my_hash){
for my $obj1(sort keys %{$my_hash{$obj0}}){
print "$obj1, $my_hash{$obj0}{$obj1} \n";
}
}
引用
正常的$obj = \%my_hash引用就可以了,解引用也可直接%hash = %$obj,或者不解直接用$obj -> {$sig1} -> {name}这样用应该也是没问题的,不过这中花哨的写法对我来说太难了,以下三种方式都可以:
my $hash = \%my_hash;
print "hello1, $hash->{amod_bmod_awsize}->{name} \n";
print "hello2, $hash->{amod_bmod_awsize}{name} \n";
print "hello3, ${$hash}{amod_bmod_awsize}{name} \n";
hello1, amod_bmod_awsize
hello2, amod_bmod_awsize
hello3, amod_bmod_awsize
初始化
多维哈希合并时候一样是这个语法(好像这些都是废话):
%hash = (%hash1, %hash2);
因此要初始化一个多维hash和之前一样:
%hash = ();
拷贝
hash进行=直接拷贝的话,是一种深层指针拷贝,看下这个代码:
delete $my_hash{$sig1}; # no use, to long
my $sig3 = "bmod_amod_buser";
$my_hash{$sig3} = $my_hash{$sig2};
print "first, copy=====================\n";
&display_list(&display_hash(\%my_hash));
print "\nfirst, delete====================\n";
delete $my_hash{bmod_amod_bresp}{width};
&display_list(&display_hash(\%my_hash));
打印结果:
first, copy=====================
bmod_amod_bresp
link
dst => amod
src => bmod
name => bmod_amod_bresp
sel
0 => 0
10 => 10
2 => 2
width => 2
bmod_amod_buser
link
dst => amod
src => bmod
name => bmod_amod_bresp
sel
0 => 0
10 => 10
2 => 2
width => 2
first, delete====================
bmod_amod_bresp
link
dst => amod
src => bmod
name => bmod_amod_bresp
sel
0 => 0
10 => 10
2 => 2
bmod_amod_buser
link
dst => amod
src => bmod
name => bmod_amod_bresp
sel
0 => 0
10 => 10
2 => 2
可以发现,拷贝是吧全部信息都拷过去了,但是$sig2 width被删除时,$sig3中对应的信息也被删除了。因此需要深度复制拷贝的话,请使用:
use Clone 'clone';