第8章 引用
假设你想制作一个简单的表(二维数组),为一组人员显示生命数据用---包括年龄,眼睛颜色,和重量等。
你可以通过先给每个队里的成员创建一个数组来实现这个目的:
@john = (47,"brown",186);
@mary =(23,"hazel",128);
在C里,间接的最常见的形式就是指针,它可以让一个变量保存另外一个变量的内存地址。
在Perl里,间接的最常见的形式是引用。
8.1 什么是引用?
@vitals = ('john', 'mary', 'bill');
在本章的术语里,引用一个数值就是创建一个指向它的硬引用。
有两个内建的函数,bless和block,它们都接受一个引用作为自己的参数,
但是都会隐含地对这些引用进行解引用以实现各自的功能
一个引用可以指向任何数据结构,因为引用是标量,所以你可以把它们保存在数组和散列里,
因此我们就可以做出数组的数组,散列的数组,数组的散列.
8.2 创建引用:
你可以用一个反斜杠创建一个指向任何命名变量或者子过程的引用。(你还可以把它用于一个匿名标量值
乍一看,这个操作符的作用类似C里的&取址操作符
$scalarref = $foo;
$constref = 186_282.42;
$arrayref = @ARGV;
$hashref = \%ENV;
$coderef = &handler;
$globref = *STDOUT;
8.2.2.1 匿名数组组合器
zjzc01:/root/big2# cat a2.pl
$arrayref =[1,2,['a','b','c','d']];
use Data::Dumper;
my $xx= Dumper($arrayref);
print "111111111
";
print $xx;
print "
";
print $$arrayref[2][1];
print "
";
zjzc01:/root/big2# perl a2.pl
111111111
$VAR1 = [
1,
2,
[
'a',
'b',
'c',
'd'
]
];
b
8.2.2.2 匿名散列组合器:
你可以用花括弧创建一个指向匿名散列的引用:
匿名散列用{};
zjzc01:/root/big2# cat a4.pl
$hashref = {
'Adam' => 'Eve',
'Clyde' => $bonnie,
'Antony' => 'Cleo' . 'patra',
};
use Data::Dumper;
my $xx= Dumper($hashref);
print "111111111
";
print $xx;
print "
";
zjzc01:/root/big2# perl a4.pl
111111111
$VAR1 = {
'Clyde' => undef,
'Antony' => 'Cleopatra',
'Adam' => 'Eve'
};
现在我们有了表示本章开头的表的另外一种方法:
zjzc01:/root/big2# cat a5.pl
$table = {
"john" => [47, "brown", 186],
"mary" => [23, "hazel", 128],
"bill" => [35, "blue", 157],
};
use Data::Dumper;
my $xx= Dumper($table);
print "111111111
";
print $xx;
print "
";
zjzc01:/root/big2# perl a5.pl
111111111
$VAR1 = {
'john' => [
47,
'brown',
186
],
'bill' => [
35,
'blue',
157
],
'mary' => [
23,
'hazel',
128
]
};
8.2.2.3 匿名子过程组合器:
你可以通过不带子过程名字的sub创建一个匿名子过程:
zjzc01:/root/big2# cat a6.pl
$coderef = sub { print "Boink!
" }; # 现在 &$coderef 打印 "Boink!"
use Data::Dumper;
my $xx= Dumper($coderef);
print "111111111
";
print $xx;
print "
";
zjzc01:/root/big2# perl a6.pl
111111111
$VAR1 = sub { "DUMMY" };
zjzc01:/root/big2# cat a6.pl
$coderef = sub { print "Boink!
" }; # 现在 &$coderef 打印 "Boink!"
$xx=&$coderef();
print "11111111111
";
print "$xx is $xx
";
zjzc01:/root/big2# perl a6.pl
Boink!
11111111111
$xx is 1
8.2.3 对象构造器:
子过程也可以返回引用, 但是有时候别人要求你用一个子过程来创建引用而不是由你自己创建引用
特别是那些叫做构造器的特殊子过程创建并返回指向对象的引用
对象只是一种特殊的引用,它知道自己是和哪个类关联在一起的,而构造器知道如何创建那种关联关系。
这些构造器是通过使用bless操作符,将一个普通的引用物转换成一个对象实现的,
所以我们可以认为对象是一个赐过福的引用。
构造器通常叫做new--特别是C++程序员更是如此看待
构造器可以用下列任何方法调用:
$objref = Doggie::->new(Tail => 'short', Ears => 'long'); #1
$objref = new Doggie:: Tail => 'short', Ears => 'long'; #2
$objref = Doggie->new(Tail => 'short', Ears => 'long'); #3
$objref = new Doggie Tail => 'short', Ears => 'long'; #4
8.2.4 句柄引用
8.2.5 符号表引用:
8.3.1 把一个变量当作变量名使用:
因此不仅仅 $$foo 是指 $foo 指向的标量值,
@$bar 是 $bar 指向的数组值, %$glarch 是 $glarch 指向的散列数值,等等。结果
是你可以在任何简单标量前面放上一个额外的趣味字符将它解引用:
$foo = "three humps";
$scalarref = $foo; # $scalarref 现在是一个指向 $foo 的引用
$camel_model = $$scalarref; # $camel_model 现在是"three humps"
8.3.3 使用箭头操作符:
对于指向数组,散列,或者子过程的引用,第三种解引用的方法涉及到使用->中缀操作符
zjzc01:/root/big2# cat a7.pl
@table=(a,b,c,{"English"=>["a1","a2","a3"] ,"Chinese"=>"bb"});
use Data::Dumper;
my $xx= Dumper($table);
print "111111111
";
print $xx;
print "
";
print "2222222222
";
print $table[3]->{"English"}->[0];
print "
";
zjzc01:/root/big2# perl a7.pl
111111111
$VAR1 = undef;
2222222222
a1
zjzc01:/root/big2# cat a8.pl
$array=[1,2,3,4];
print "111111111
";
print $array[3];
print "
";
print "222222222
";
print $array->[3];
print "
";
zjzc01:/root/big2# perl a8.pl
111111111
222222222
4
8.3.4 使用对象方法:
如果一个引用碰巧是一个指向一个对象的引用, 那么定义该对象的类可能提供了访问该对象的内部方法,
并且如果你只是使用这些类,那么通常应该坚持使用那些方法
换句话说就是要友善,并且不要把一个对象当作一个普通引用看待,
8.3.5 伪散列:
一个伪散列是一个指向数组的任意引用,它的第一个元素是一个指向散列的引用。
zjzc01:/root/big2# cat a9.pl
$john = [ {age => 1, eyes => 2, weight => 3}, 47, "brown", 186 ];
print $john->[0]->{eyes};
print "
";
zjzc01:/root/big2# perl a9.pl
2
8.3.6 硬引用可以用的其他技巧:
8.3.7 闭包:
我们早些时候谈到过用一个没有名字的sub{}创建匿名子过程。
zjzc01:/root/big2# cat a10.pl
{
my $critter = "camel";
$criterref = $critter;
};
print "$critter is $critter
";
print "$criterref is $$criterref
";
zjzc01:/root/big2# perl a10.pl
$critter is
$criterref is camel
$$critteref 的数值仍将使camel,即使在离开闭合的花括弧之后$critter消失了也如此。
但是$critteref 也可以指向一个指向了$critter的子过程:
zjzc01:/root/big2# cat a11.pl
{
my $critter = "camel";
$critterref = sub { return $critter };
}
use Data::Dumper;
my $xx= Dumper($critterref);
print "111111111
";
print $xx;
print "
";
print "22222222222
";
$yy=&$critterref;
print $yy;
print "
";
zjzc01:/root/big2# perl a11.pl
111111111
$VAR1 = sub { "DUMMY" };
22222222222
camel
8.3.7.2 嵌套的子过程:
命名的子过程并不合适做嵌套,但是匿名子过程却可以。