引用和嵌套数据结构
列表无法包含列表作为其元素,array也同样无法包含其他array和hash作为其元素,它们只能包含scalar。看看我们尝试下面的做法会发生什么:
my @outer = ("Sun", "Mercury", "Venus", undef, "Mars");
my @inner = ("Earth", "Moon");
$outer[3] = @inner;
print $outer[3]; # "2"
$outer[3]
是个scalar,因此它需要一个scalar值。当你尝试将@inner
这样的array值赋给它,@inner
就会在scalar上下文中被求值,这就与将scalar @inner
是同样的效果。这相当于求出了array @inner
的长度,也就是2。
然而,scalar变量可以包含任何变量的引用,包括array和hash。在Perl中,复杂的数据结构就是这样被构造出来的。
我们用反斜杠来创建一个引用。
my $colour = "Indigo";
my $scalarRef = \$colour;
如果你能够使用某个变量名,你可以加一些花括号,把一个变量的引用放进去。
print $colour; # "Indigo"
print $scalarRef; # 输出可能是 "SCALAR(0x182c180)"
print ${ $scalarRef }; # "Indigo"
如果结果没有歧义的话,你甚至可以直接省略掉花括号:
print $$scalarRef; # "Indigo"
如果是一个对array或者hash的引用,你可以用花括号或者更加风靡的箭头运算符->
:
my @colours = ("Red", "Orange", "Yellow", "Green", "Blue");
my $arrayRef = \@colours;
print $colours[0]; # 直接访问array元素
print ${ $arrayRef }[0]; # 通过引用访问array元素
print $arrayRef->[0]; # 与上一句等价
my %atomicWeights = ("Hydrogen" => 1.008, "Helium" => 4.003, "Manganese" => 54.94);
my $hashRef = \%atomicWeights;
print $atomicWeights{"Helium"}; # 直接访问hash元素
print ${ $hashRef }{"Helium"}; # 通过引用访问hash元素
print $hashRef->{"Helium"}; # 与上一句等价 - 这种写法相当常见
声明数据结构
这里有4个例子,不过现实中最后一个最有用。
my %owner1 = (
"name" => "Santa Claus",
"DOB" => "1882-12-25",
);
my $owner1Ref = \%owner1;
my %owner2 = (
"name" => "Mickey Mouse",
"DOB" => "1928-11-18",
);
my $owner2Ref = \%owner2;
my @owners = ( $owner1Ref, $owner2Ref );
my $ownersRef = \@owners;
my %account = (
"number" => "12345678",
"opened" => "2000-01-01",
"owners" => $ownersRef,
);
显然可以不用这么费劲,这段代码可以简化为:
my %owner1 = (
"name" => "Santa Claus",
"DOB" => "1882-12-25",
);
my %owner2 = (
"name" => "Mickey Mouse",
"DOB" => "1928-11-18",
);
my @owners = ( \%owner1, \%owner2 );
my %account = (
"number" => "12345678",
"opened" => "2000-01-01",
"owners" => \@owners,
);
用不同的符号声明匿名的array和hash也是可行的。用方括号声明匿名array,而用花括号声明匿名hash,这两种方法返回的是声明的匿名数据结构的引用。看仔细了,下面的代码声明的%account
和上面的完全等价:
# 花括号表示匿名hash
my $owner1Ref = {
"name" => "Santa Claus",
"DOB" => "1882-12-25",
};
my $owner2Ref = {
"name" => "Mickey Mouse",
"DOB" => "1928-11-18",
};
# 方括号表示匿名array
my $ownersRef = [ $owner1Ref, $owner2Ref ];
my %account = (
"number" => "12345678",
"opened" => "2000-01-01",
"owners" => $ownersRef,
);
或者写得更加简短(这也是你真正应该用来声明复杂数据结构的方法):
my %account = (
"number" => "31415926",
"opened" => "3000-01-01",
"owners" => [
{
"name" => "Philip Fry",
"DOB" => "1974-08-06",
},
{
"name" => "Hubert Farnsworth",
"DOB" => "2841-04-09",
},
],
);
从数据结构中获取信息
现在我们假设你还在折腾那个%account
,而且其他东西都不在作用域内(如果还有其他东西的话)。你可以逆向操作解引用,以取得每一项需要打印的信息。同样,这里有4个例子,其中最后一个最有用:
my $ownersRef = $account{"owners"};
my @owners = @{ $ownersRef };
my $owner1Ref = $owners[0];
my %owner1 = %{ $owner1Ref };
my $owner2Ref = $owners[1];
my %owner2 = %{ $owner2Ref };
print "Account #", $account{"number"}, "\n";
print "Opened on ", $account{"opened"}, "\n";
print "Joint owners:\n";
print "\t", $owner1{"name"}, " (born ", $owner1{"DOB"}, ")\n";
print "\t", $owner2{"name"}, " (born ", $owner2{"DOB"}, ")\n";
或者写得更简短些:
my @owners = @{ $account{"owners"} };
my %owner1 = %{ $owners[0] };
my %owner2 = %{ $owners[1] };
print "Account #", $account{"number"}, "\n";
print "Opened on ", $account{"opened"}, "\n";
print "Joint owners:\n";
print "\t", $owner1{"name"}, " (born ", $owner1{"DOB"}, ")\n";
print "\t", $owner2{"name"}, " (born ", $owner2{"DOB"}, ")\n";
或者使用引用和->
运算符:
my $ownersRef = $account{"owners"};
my $owner1Ref = $ownersRef->[0];
my $owner2Ref = $ownersRef->[1];
print "Account #", $account{"number"}, "\n";
print "Opened on ", $account{"opened"}, "\n";
print "Joint owners:\n";
print "\t", $owner1Ref->{"name"}, " (born ", $owner1Ref->{"DOB"}, ")\n";
print "\t", $owner2Ref->{"name"}, " (born ", $owner2Ref->{"DOB"}, ")\n";
如果我们完全跳过那些中间值,代码看起来就是这样:
print "Account #", $account{"number"}, "\n";
print "Opened on ", $account{"opened"}, "\n";
print "Joint owners:\n";
print "\t", $account{"owners"}->[0]->{"name"}, " (born ", $account{"owners"}->[0]->{"DOB"}, ")\n";
print "\t", $account{"owners"}->[1]->{"name"}, " (born ", $account{"owners"}->[1]->{"DOB"}, ")\n";
如何用array的引用作茧自缚
这个数组有5个元素:
my @array1 = (1, 2, 3, 4, 5);
print @array1; # "12345"
然而这个array只有1个元素(一个含有5个元素的匿名array的引用):
my @array2 = [1, 2, 3, 4, 5];
print @array2; # e.g. "ARRAY(0x182c180)"
这个scalar是一个含有5个元素的匿名array的引用:
my $array3Ref = [1, 2, 3, 4, 5];
print $array3Ref; # e.g. "ARRAY(0x22710c0)"
print @{ $array3Ref }; # "12345"
print @$array3Ref; # "12345"