Perl引用详解
Perl中提供了一种类似于C/C++语言中的指针功能的数据类型,名叫引用;这个引用与C++中的引用的概念不一样,但是效果一样。Perl中的引用就是指针,它用于跟踪变量在内存中的地址,可以跟踪简单的标量变量、数组变量、哈希变量、文件句柄变量,甚至还可以跟踪子程序。引用存放的是某个变量在内存中的地址,这对于跟踪大型的数据对象是很有用的。引用也是一种数据类型,这种类型的变量叫做引用变量,引用变量中存放的是内存地址,而不论被引用的数据对象是何种类型的数据对象,不论被引用的数据对象所占用的内存空间有多大,引用变量中存放的总是被引用的数据对象的内存地址。内存地址是个无符号的整数,所以引用型变量总是一个简单的标量变量,与其它普通的标量变量的使用方法完全相同。如果在程序中使用了引用,那么Perl解释器不再通过变量的名字来调用变量的值,而是是通过引用变量中所存放的内存地址来调用变量的值。总之,引用变量可以跟踪变量在内存中的地址,它可以跟踪任何类型的变量;引用变量总是一个标量,用于存放另一对像在内存中的地址,引用变量之占用内存中的一个地址空间,而无论存放的是一个标量变量的地址还是存放其它大对象的内存地址;
引用的使用
如果标量变量$Pointer中存放的是一个数组的地址,那么在访问数组元素的时候,要先提取数组的地址,然后再通过地址来访问数组中的元素;如:可以通过形式“@$Pointer”来访问数组中的元素;形式“@$Pointer”的意义为:取出$Pointer中存放的地址作为数组使用。类似地,如果$Pointer中存放的是某个哈希变量的地址,那么可以通过形式“%$Pointer”来访问哈希变量中的元素。
常见数据类型的引用定义:
数据类型----定义----举例
标量变量----\$Var----$Pointer=\$Var
数组----\@Array----$Pointer=\@Array
哈希变量----\%Hash----$Pointer=\%Hash
文件句柄----\*FILEHANDLE----$Pointer=\<FILEHANDLE>
常量----\常量----$Pointer=\3.1415926
子程序----\&SubRoutine----$Pointer=\&SubRoutine
匿名数组----[ LIST ]----$Pointer=[ “Smith”,“Jack”,“Jimmy”,“ZhaZha”]
匿名哈希变量----{ Key=>Value }----$Pointer = { Key1=>Value1,Key2=>Value2}
匿名子程序----sub {} ----$Pointer=sub { printf( “Hello,Perl World\n”);}
对象引用----bless----$self;
引用变量的类型:
1、粗略地分,可以分为硬引用和符号引用;符号引用中包含变量的名字,符号引用就类似与文件名或操作系统的软连接,而硬引用则类似与操作系统中的硬连接;硬引用跟踪引用的计数,当引用计数为0时,Perl自动将被引用的项目所占用的内存释放掉;如果被引用的项目是某个类的对象,那么Perl在释放该对象时会自动调用该对象的析够函数,以释放其内存;Perl本身就是一个面向对象的语言,Perl中的一切都是对象,而包和模块是对象更易于使用;
2、如果按照引用变量中所存放的地址类型来分,引用变量可以分为6种类型:
1)直接引用型变量:顾名思义,直接引用变量保存了其它变量的地址,包括标量、数组变量、哈希变量、文件句柄变量以及其它类型的变量;直接引用变量可以修改被引用的变量的值,引用变量中的地址值也可以被修改;直接引用变量使用反斜杠“\”来定义。
对于数组和哈希变量的直接引用可以这样定义:
$lpArray=\@Array;#$lpArray存放了数组@Array的首地址,$lpArray代表数组名;
此时数组元素的访问方式有:$lpArray->[$Index] 或 $$lpArray[$Index] 或 ${$lpArray}[$Index];
$lpHash = \%Hash;#$lpHash存放了哈希变量%Hash的首地址,$lpHash代表哈希变量的名称;
此时哈希变量的元素的访问方式有:$lpHash->{$key} 或 $$lpHash{$key} 或 ${$lpHash}{$key};
2)常量引用型变量:它引用的是直接数,而不是变量;也使用反斜杠“\”定义;常量引用变量所保存的地址值可以被修改,但是被引用的常量值是不能被修改的,因为被引用的是常量;它的存储方式决定了它是被存放再一块受保护的内存空间中的。
3)子程序引用型变量:它是直接引用变量和常量引用变量的混合;子程序引用变量不能用来修改子程序引用变量所指向的子程序,但是子程序本身是可以被修改的;生成子程序引用型变量时,必须使用位与运算符“&”来定义所引用的是子程序而不是直接数。
4)符号引用型变量:符号引用型变量是包含另一个变量的直接数名称的变量;Perl中使用反斜杠“\”和变量名称来定义引用符号。
5)匿名引用型变量:匿名引用变量可以生成不命名的对象引用,这些对象也被成为是孤立的对象;匿名引用有匿名数组、匿名哈希变量和匿名子程序;分别使用运算符“[]”、“{}”和 "sub {}”。匿名引用运算符的使用是上下文相关的,只在给标量变量赋值的时候才生成引用。匿名子程序运算符要求在子程序定义内用分号结尾;Perl把匿名引用变量当作在内存地址中有特定地址的语句。
6)对象引用变量:对象引用变量就是匿名哈希变量引用的一个典型应用;Perl中使用bless操作符来专门处理这类引用,可以生成引用对象与生成对象的类之间的连接;
间接引用:
访问引用所指向的变量的值,称为间接引用;要取得内存中引用变量最终所指向的数值,就必须告诉Perl解释器查找作为数值容器的引用变量所保存的内存地址;实际情况是这样的,Perl中的每一个“$”符号就是一个数值容器,Perl解释器从右到左,或从内到外来顺序地解释每一个“$”符号。每次遇到“$”符号时,都会把$右边的对象作为一个数值容器,比如:$$Pointer,Perl就会把Pointer当作一个数值容器,从右边第一个$开始取得对象Pointer中存放的地址,当遇到左边的$时,又会把$Pointer当作一个数值容器,而$Pointer中存放的时地址,左边的$表示一个数值容器,所以${$Pointer}就取到了引用变量所指对像中保存的数值内容了。
注意:指针就是地址,通过指针可以访问到该地址处所存放的数据;如果指针指向了无效的内存地址,就会得到不正确的数据,通常情况下,Perl会返回null值,但是并不依赖于此,一定要在程序中正确地初始化所有的指针,使之指向有效的数据项。
匿名数组和匿名哈希变量:
匿名数组:$lpArray=[1,2,"A","B",(4 .. 10),[12 .. 20]];
匿名哈希变量:$lpHash={Key1=>Value1,Key2=>Value2,Key3=>Value3,....,KeyN=>ValueN};
理解各种数据类型的引用与解引用
reference是另一个变量的地址。reference可以指向array,hash,或perl代码段,reference使perl代码运行更快。
一)、array的reference和dereference
1)array的reference
正常地,我们存储list的元素在array中如下:@array = (“one”,”two”,”three”,”four”,”five”);
使用\来将array的地址赋给reference变量,如下:$array_ref = \@array;
如果你print $array_ref,将显示如下:ARRAY(0x1a2b3c);array的reference可以被传递给subroutine,如下:
sub add_numbers{
my $array_ref = shift;
.....
}
@numbers = (11,2,3,45);
$array_ref = add_numbers(\@numbers);
在以上的代码中,我们需要对array的reference进行dereference,然后才可以使用array中的元素。
下面是传递array的reference到subroutine的优点:
* 如果array传递给subroutine,perl将整个array重新copy到@_中,当array比较大时,这将非常的低效。
* 当我们需要在subroutine中修改原来的array的时候,我们需要传递array的reference。
* reference其实是构造复杂数据结构的本质。
我们也可以将匿名的array赋给reference如下:$array_ref = [11,2,3,45]
2)array的reference的dereference
在subroutine中,我们可以使用如下的方法来dereference array的reference:@{ $array_ref };
取得第一个元素如下:${ $array_ref }[0]
或者可以使用perl的特殊符号来使用array的reference,如下:
# Get all the elements of @numbers array.
@{$_[0] }
# Get a particular element. This gives the first element of the array.
${$_[0]}[0]
注意,如果只是dereference一个简单的scalar变量,可以忽略括号,如下:
@$array_ref # same as @{$array_ref}
$$array_ref # same as ${$array_ref}
$_[0] # not a simple scalar variable and this cant be dereferenced,
二)、hash的reference和dereference
hash的reference和dereference与array的一样。
hash的reference如下:
%author = (
'name' => "Harsha",
'designation' => "Manager"
);
$hash_ref = \%author;
dereference后访问元素如下:
$name = ${$hash_ref}{name}; 等价于 my $name = $hash_ref->{name};
访问所有的keys如下:
my @keys = keys %{$hash_ref}; #等价于my @keys = keys %author;
如果是简单scalar变量,可以忽略括号,如下:my @keys = keys %$hash_ref; $name = $$hash_ref{name};
匿名hash的reference如下:
my $hash_ref={
'name' => "Harsha",
'designation' => "Manager"
};
使用如下:$name = ${$hash_ref}{name};
三)、对reference变量使用 -> 来获得属性
my $name = $hash_ref->{name};
小结
一个引用就是创建一个硬链接,一个引用只有在对它的所有引用都消失之后才会被摧毁,创建的引用只是一个标量,对这个标量进行解引用意味着我们使用这个引用访问引用物。
创建引用的方法:
反斜杠:对任何命名变量,数值,匿名标量值,对一列表用反斜杠,会得到一组引用
匿名数组组合器,使用方括号创建一个指向匿名数组的引用
匿名散列组合器,使用花括号创建一个指向匿名散列的引用
匿名子过程组合器,通过不带子过程名字的sub创建一个匿名子过程
对象是一个赐过福的引用
过引用同名类型团来创建指向文件句柄或者目录句柄
引用的高级技术
当编译器看到双引号字串里的 @{...} 的时候,它就会被当作一个返回一个引用的块分析
print "We need @{ [$n + 5] } widgets!/n";
用use strict 'refs';来拒绝符号引用
嵌套子过程
Tie::RefHasn 可以将引用作为键值,其他的不行
循环引用导致内存释放有问题的
使用引用的方法:
把变量当做变量名来解析
$bar = $$scalarref;
push(@$arrayref, $filename);
$$arrarref[0] = "January"; # 设置 @$arrayref 的第一个元素
@$arrayref[4..6]=qw/May June July/; # 设置若干个 @$arrayref 的元素
%$hashref = (KEY => "RING", BIRD => "SING"); # 初始化整个散列
$$hashref{KEY} = "VALUE"; # 设置一个键字/数值对
@$hashref{"KEY1", "KEY2"} = {"VAL1", "VAL2"}; # 再设置两对&$coderef(1,2,3);
print $handleref "output/n";
${$hashref{KEY}} 和 ${$hashref}{KEY},$$hashref{KEY} 区别,后者正确。
把一个block{}当做变量名来使用。
$bar = ${$scalarref};
push(@{$arrayref}, $filename);
${$arrayref}[0] = "January";
@{$arrayref}[4..6] = qw/May June July/;
${$hashref}{"KEY"} = "VALUE";
@{$hashref}{"KEY", "KEY2"} = ("VAL1", "VAL2");
&{$coderef}(1,2,3);
使用箭头操作符。解引用的类型是由右操作数决定的,也就是由直接跟在箭头后面的东西决定。如果箭头后面的东西是一个方括弧或者花括弧,那么左操作数就分别当作一个指向一个数组或者散列的引用,由右边的操作数做下标定位。如果箭头后面的东西是一个左圆括弧,那么左操作数就当作一个指向一个子过程的引用看待,然后用你在圆括弧右边提供的参数进行调用。
arrayref->[2] = "Dorian"; #3
$hashref->{KEY} = "F#major"; #3
$coderef->(Presto => 192); #3 在方括弧或花括弧之间,或者在一个闭方括弧或花括弧与圆括弧之间的箭头是可选的,后者表示间接的函数调用。
再识引用与解除引用(本节转自骏马金龙博客)
在perl中只有3种基本的数据结构:标量、数组、hash。变量可以是数值,可以是字符串。但由这3种基本结构就可以构造出更复杂的数据结构,例如hash中用数组做value,数组中用hash做元素。但perl对于底层的一些数据存储,很多时候对这些数据是直接拷贝存储的。而有些时候是没必要去拷贝数据的,通过引用,可以避免拷贝操作,哪里需要数据,用数据对象的引用即可,也就是插一个"指针"的事。
如何表示数组和hash的引用
引用就像是指针,对一个引用进行输出,在字符串上下文将返回它指向的目标数据的类型和内存地址,在数值上下文将返回地址空间。例如,数组的引用和hash的引用,将数组@name的引用赋值给了一个名为$arr_ref的标量,将hash@member的引用赋值给了一个名为$hash_ref的标量。注意,引用都是标量。
引用的方式很简单,只需在 数组的@符号、hash的%符号之前加上反斜线\ 。例如\@name就表示这是名称为name的数组的引用,\%member就表示这是名称为member的hash的引用。
@name=qw(longshuai xiaofang freeoa tuner);
%member=(
longshuai => "18012345678",
xiaofang => "17012345678",
freeoa => "16012345678",
tuner => "15012345678"
);
$arr_ref=\@name; # 将数组的引用赋值给一个标量
$hash_ref=\%member; # 将hash的引用赋值给一个标量
print "$arr_ref","\n"; # 输出:ARRAY(0x18a5fa0)
print "$hash_ref","\n"; # 输出:HASH(0x18a6060)
由于引用被当作一个标量,所以能使用标量的地方,就能使用引用。例如将一个数组的引用放进hash中:
@name=qw(longshuai xiaofang freeoa tuner);
$arr_ref=\@name;
%member=(
longshuai => "18012345678",
xiaofang => "17012345678",
freeoa => "16012345678",
tuner => $arr_ref
);
print "$member{'tuner'}","\n"; # 输出数组的引用:ARRAY(0x13d7fa0)
除数组、hash之外的引用
除数组、hash有引用,标量、函数也有引用,而且还不止这些,比如文件句柄引用、正则表达式引用等。目前暂时只需了解标量、数组、hash、函数的引用方式即可,分别为:
\$x # 引用 scalar
\@y # 引用 array
\%z # 引用 hash
\&f # 引用 function
标量引用的一个技巧
下面创建一个指向$foo1值的引用$ref_foo1,也就是说$ref_foo1和foo1是等价的,要引用这个变量的值,需要使用$$ref_foo1或$foo1。
my $ref_foo1 = \$foo1;
my $foo1="xyz";
my $ref_foo1 = \$foo1;
print "$$ref_foo1\n";
经常会看到下面一种代码格式:
my $ref_foo2 = \my $foo2;
等号右边\my $foo2表示先创建一个用my修饰的变量$foo2(因为未赋值,所以初始化为undef),再创建一个指向它的引用$ref_foo2。和上面的示例相比,它只是多了一个临时创建变量的功能,因为my的优先级高于\,它等价于\(my $foo2)。上面的语句还可以带上初始值:
my $ref_foo2 = \(my $foo2="abcd");
print "$$ref_foo2\n";
引用计数器和引用作用域
可以为同一段数据对象创建多个引用,由于每个引用都指向同一段数据对象,所以相同数据对象的引用是完全一致的。由于引用在数值上下文返回引用对象的地址空间,所以可以用比较操作符==来比较引用,当然使用eq来比较也完全可以:
@name=qw(longshuai xiaofang freeoa tuner);
$arr_ref=\@name;
$arr_ref1=\@name;
$arr_ref2=$arr_ref;
print "ref equals\n" if $arr_ref == \@name; # true
print "ref equal ref1\n" if $arr_ref == $arr_ref1; # true
print "ref1 equal ref2\n" if $arr_ref1 == $arr_ref2; # true
在perl中,对每段数据的引用都会维护一个引用计数器:
1.最初创建数据对象时,赋值给初始化数组、hash,引用数为1
2.以后每次引用、赋值引用、拷贝引用等操作都会对引用数加1
3.将其赋值为undef,则显式取消引用关系,引用数减1
4.引用有作用域,当离开作用域时,该作用域内的引用都将取消(可看作是赋值型的标量,当定义my时,超出边界就失效)
5.只要引用计数器还未减少为0,用于存储数据对象所占用的内存就不会释放。当引用计数为0时,perl将回收这段内存空间,但不会交还给操作系统,perl再需要的时候会重用这段内存空间存储新数据,而无需重新向操作系统申请开辟新内存
这和Unix下的硬链接类似,每创建一个硬链接,硬链接数量就加1,每删除一个硬链接,硬链接数量就减1。
use 5.010;
@names=qw(longshuai xiaofang freeoa tuner); # ref counter = 1
$arr_ref1=\@names; # ref counter = 2
$arr_ref2=$arr_ref1; # ref counter = 3
$arr_ref3=\@names; # ref counter = 4
$arr_ref2=undef; # ref counter = 3
@names=undef; # ref counter = 2
say "arr: @names"; # 输出:arr:
say "ref1: $arr_ref1"; # 输出:ARRAY(0x55befad8f0a0)
say "ref2: $arr_ref2"; # 输出:ref2:
say "ref3: $arr_ref3"; # 输出:ARRAY(0x55befad8f0a0)
如果使用my或local这样作用域关键词去定义引用,当引用超出对应边界后,就会失效,引用计数器就会对应减1。当然,如果不定义为my或local,则超出边界也不会失效。
use 5.010;
@names=qw(longshuai xiaofang freeoa tuner); # ref counter = 1
$arr_ref1=\@names; # ref counter = 2
{
my $arr_ref2=$arr_ref1; # ref counter = 3
my $arr_ref3=\@names; # ref counter = 4
} # ref counter = 2
say "ref1: $arr_ref1";
say "ref2: $arr_ref2"; # arr_ref2已经失效
say "arr: @names";
say "ref3: $arr_ref3"; # arr_ref3已经失效
有一点需要注意,就是将引用作为子程序(函数)的参数时,它首先将引用赋值给@_特殊变量,这时引用计数会加1,当退出子程序时,@_将失效,引用计数器将减1。
&mysub(\@names); # 直接将引用赋值给@_
&mysub($arr_ref); # 拷贝引用,@_也将引用数据对象
引用计数管理内存的优缺点
perl采用引用计数的方式回收内存空间。引用计数管理内存的方式已经存在了很久,和GC(Garbage collection)垃圾回收机制是同一年(1960年)被研究出来的。无论是哪种内存管理机制,都有优点,也都有缺点。
对于引用计数管理方式而言,它最大的优点在于:
1.即刻回收:只要数据对象的引用计数器为0了,就会立刻被回收
2.暂停时长很短:因为回收速度时无需遍历内存,所以负责回收的工作效率很高
使用引用计数管理内存最大的缺点在于:
1.因为要频繁增、减计数,负责增、减的工作压力非常大
2.无法回收循环引用(引用环路问题,见下文)
相比于引用计数管理内存的方式,GC回收机制都是在内存不够后,遍历整个内存来回收的,所以有延迟性和低效性(有好几种改进的GC,都各有优缺点,但原始的GC算法就是如此)。其实无论是引用计数还是GC,都对它们各自的缺点有各种改进,但修补缺陷的同时,也会损伤它们的优点,所以不同语言针对不同适用场景,总是采用不同的折衷手段。
回到引用计数的一个缺点问题上:无法回收循环引用问题。所谓循环引用,是指A引用B的对象,B又引用A的对象。例如:
@name1=qw(longshuai freeoa);
@name2=qw(xiaofang tuner);
push @name1,\@name2;
push @name2,\@name1;
验证下:
use 5.010;
@name1=qw(longshuai freeoa);
@name2=qw(xiaofang tuner);
say "name1_1: ",\@name1; # ARRAY(0xNAME1)
say "name2_1: ",\@name2; # ARRAY(0xNAME2)
push @name1,\@name2;
push @name2,\@name1;
say "name1_2: @name1"; # ARRAY(0xNAME2)
say "name2_2: @name2"; # ARRAY(0xNAME1)
这样的引用环路,使得@name1和@name2对应的数据对象的引用,无论如何也无法减少到0。这会导致一个重大问题:内存泄漏(memory leak)。随着泄漏越来越多,内存终有被耗尽的时刻。也许真正写成内存泄漏的代码比较少,但很多时候构建复杂的数据结构时,无意中可能就会出现引用环路问题。要避免引用环路,可以使用perl的另一种引用行为:弱引用(weak)。或者,在退出引用作用域之前,打破引用环路。例如:
{
@name1=qw(longshuai freeoa);
@name2=qw(xiaofang tuner);
push @name1,\@name2;
push @name2,\@name1;
......
# 退出作用域之前,在引用的内部清空引用数据
@name1=();
@name2=();
}
这样,在退出作用域后,@name1=qw(longshuai freeoa (这段空)),同理@name2。如此一来,@name1和@name2又变回了最初状态,都只有一个引用,且没有了循环引用。当然上面是直接清空初始引用的,如果push进去的是引用变量,则清空引用变量即可。
{
@name1=qw(longshuai freeoa);
@name2=qw(xiaofang tuner);
$name1_ref=\@name1;
$name2_ref=\@name2;
push @name1,$name1; # 使用引用变量,而非初始的数组名引用
push @name2,$name2;
......
# 退出作用域之前,在引用的内部清空引用数据
$name1=undef;
$name2=undef;
}
要检查是否出现内存泄漏问题,可以使用Test::Memory::Cycle模块。perl目前还没有其它垃圾回收机制(garbage collection),也许在未来的版本中可能会引入新的gc管理方式来替代引用计数的管理方式。
解除引用:从引用还原到数据对象
上面对使用引用可以指向数据对象,但如果想通过引用的方式取出数据对象的值呢,想要输出@name2中包含的@name1的元素,而不是它的地址空间。这需要解除引用(dereference),将$ref_name引用还原为数据对象qw(longshuai freeoa)。
解除数组的引用
在解释引用解除之前,有必要先解释下引用符号和数据对象的名称。
创建一个@name=qw(value1 value2 value3)的数组,这个数组初始化时,数组名为name,它是这个列表数据对象的第一个引用(注意,数组名是引用),引用方式为@符号+数组名name,即@name。实际上,真正规范的引用方式为@{name}。因此,当使用引用变量的方式引用数据对象时,只需把数组名称换成引用变量的名称即可(它们是等价的,都是对数据对象的引用),再加上@符号即可,因为数组名称和引用变量的名称都是指向数据对象的名称。例如,将@name的引用赋值给一个引用变量$ref_name后(即$ref_name=\@name),就可以使用@$ref_name的方式来表示解除引用,它和@name是等价的关系。
以下几种引用方式绝大多数时候是等价的,可以用来做参考、对应。其实很好理解,只要把原本的对象名称,替换成引用变量的名称即可。
@{name} --> @{$ref_name}
@{ name } --> @{ $ref_name }
@name --> @$ref_name
如果引用变量的名称有特殊符号,例如以$符号作为变量名的开头符号,则不能省略大括号。当然这种情况基本不会出现,因为不符合命名规范,没人会自找麻烦。
use 5.010;
@name=qw(longshuai freeoa);
$ref_name=\@name;
say "@{$ref_name}";
say "@{ $ref_name }";
say "@$ref_name";
解除hash的引用
同理,解除hash的引用同解除数组引用的方式一样,使用引用符号%+引用的名称即可。如创建一个hash:%myhash,其中myhash是hash数据对象的名称,%是hash的引用符号。创建一个hash的引用变量$ref_myhash=\%myhash,那么引用变量对应的是hash的名称myhash,所以通过引用变量名来解除hash引用时:%$ref_myhash即可。以下是几种等价的引用方式:
%{name} --> %{$ref_name}
%{ name } --> %{ $ref_name }
%name --> %$ref_name
use 5.010;
%myhash=(
longshuai => "18012345678",
xiaofang => "17012345678",
freeoa => "16012345678",
tuner => "15012345678"
);
$ref_myhash =\%myhash;
say %$ref_myhash;
解除引用:取数组、hash中的元素(1)
对于普通的数组和hash,取得它们数据对象中的元素方式为:
$arr_name[1] # 取得数组中的第二个元素
${arr_name}[1] # 取得数组中的第二个元素
$hash_name{'mykey'} # 取得hash中key为'mykey'的value
${hash_name}{'mykey'} # 取得hash中key为'mykey'的value
那么使用引用的方式来获取数组、hash中的元素时,方式是一样的,只需将引用变量替换为对应的数据对象名称即可。
# 数组元素的引用
${name}[1] -> ${$ref_name}[1]
${ name }[1] -> ${ $ref_name }[1]
$name[1] -> $$ref_name[1]
# hash元素的引用
${myhash}{'freeoa'} -> ${$ref_myhash}{'freeoa'}
${ myhash }{'freeoa'} -> $ {$ref_myhash }{'freeoa'}
$myhash{'freeoa'} -> $$ref_myhash{'freeoa'}
use 5.010;
# 取数组的元素
@name=qw(longshuai freeoa);
$ref_name=\@name;
say "${$ref_name}[1]";
say "${ $ref_name }[1]";
say "$$ref_name[1]";
# 取hash的元素
%myhash=(
longshuai => "18012345678",
xiaofang => "17012345678",
freeoa => "16012345678",
tuner => "15012345678"
);
$ref_myhash =\%myhash;
say "${$ref_myhash}{'freeoa'}";
say "${ $ref_myhash }{'freeoa'}";
say "$$ref_myhash{'freeoa'}";
解除引用:取数组、hash中的元素(2)
除了上面的介绍的取元素方法,对于引用变量,还支持更简洁的"瘦箭头"指向法:只需使用引用变量名->元素索引即可。注意,箭头两边必须不能有空格。
$ref_name->[1]
$ref_myhash->{'mykey'}
如此一来,取元素的写法变得简洁易读,且可以规范化,也不会再出现一大堆容易让人疑惑的符号。当然,到目前为止都只是简单的引用方式,稍后介绍如何取复杂数据结构的元素时,将会看到不用箭头的方式取数据将非常伤眼睛。关于瘦箭头取元素的用法,给个简单的示例:
use 5.010;
@name=qw(longshuai freeoa);
$ref_name=\@name;
say "$ref_name->[0]";
%myhash=(
longshuai => "18012345678",
xiaofang => "17012345678",
freeoa => "16012345678",
tuner => "15012345678"
);
$ref_myhash =\%myhash;
say "$ref_myhash->{'freeoa'}";
取复杂数据结构中的元素
对于复杂数据结构,想要取它的元素并非那么容易。例如,hash的value中有数组作为元素,该数组元素中又有数组的时候。需要特别说明的是,当将复杂数据结构的引用当作子程序的参数传给@_时,甚至是@_的一部分时,要特别小心地写,一个不小心就错了。如子程序传递的参数形式如下:
mysub('aaa',\%Config)
那么要在子程序内部取得more_urllist中的第二个元素,子程序中取元素相关的代码大致如下:
my $first_arg = shift @_;
say "${$_[0]}{'urllist'}"; # 等价于:$_[0]->{'urllist'}
say "${$_[0]}{'urllist'}[1][3][1]"; # 等价于:$_[0]->{'urllist'}[1][3][1]
只需明白,@_中的元素使用$_[N]获取,而$_[N]获取到的可能是以一个引用,所以将其当作引用变量名即可。