perl5的引用(perlreftut)

名称

关于Perl5中引用的简短教程

描述

关于Perl5中一个非常重要的新功能就是引用,我们可以用引用来完成操作复杂的数据结构,比如多为数组和嵌套哈希。但是这要求我们学习一些新的语法。废话少说,开始吧。

为什么Perl5里要加入引用?

Perl4里,你的哈希列表里的值只能是标量,不能是列表,也就是不能嵌套。懂?不懂看下面

下面有一张城市和其对应国家的文件

1 Chicago, USA
2 Frankfurt, Germany
3 Berlin, Germany
4 Washington, USA
5 Helsinki, Finland
6 New York, USA

然后你想读取这个文件,输出如下的格式,即把国家放在前面,然后是这个国家里城市名

1 Finland: Helsinki.
2 Germany: Berlin, Frankfurt.
3 USA: Chicago, New York, Washington.

一种办法就是你创建一个新的哈希,然后国家名就是这个新哈希的键,它的值就是与之相关的城市名所组成的列表。每次你从文件中读取一行数据,以逗号分割为城市名和国家名,然后判断你新创建的哈希里有没有这个国家名的键,没有就创建,然后把城市名加在与其所对应的值的列表里。大致过程就是这样了。

但是这是有前提的,就是哈希的值可以是一个列表,就好像你建了个数组,数组里的第一个元素又可以是另一个数组。但是在Perl4的时代这是不可能的。Perl4里如果你要完成类似的行为你可能会把多个城市名以逗号拼接成join一个字符串,然后把这个当成哈希的值存进去。

解决办法

当Perl5来临的时候,我们已经改变不了一些既定的事实了,那就是哈希里的值必须是标量scalar。所以解决问题的办法就是用引用。

引用也是标量,所以他没有违反以前的游戏规则。引用是什么,其实就像是人的名字,当你叫李三的时候,这里的李三就是李三这个人的引用。Perl里的引用都可以用来引用哪些类型的数据呢?可以引用数组,哈希。基本上是任何东西都可以被引用。

引用就像是你给其他东西取个了名字,比如你给一个数组或是哈希取了个名字叫jack,那么你就可以用jack来获取到这个数组或是哈希。现在来看看有关引用的语法。

语法

Perl提供了两种办法来创建引用,两种办法来使用引用。

创建引用

办法 1

在变量前添加反斜杠来获取该变量的引用

$aref = \@array; # $aref 现在就是 @array 的引用了, 以下同理
$href = \%hash;
$sref = \$scalar; 

一旦创建成功,那你就可以自由的创建和复制这些引用了,就像普通的标量一样。

$xy = $aref; # $xy 现在也是 @array 的引用
$p[3] = $href; # $p[3] 现在也是 %hash 的引用
$z = $p[3]; # $z 现在同样也是 %hash 的引用

这些例子都是通过已有的标量名来创建引用的,其实你可以直接为一组数据创建引用,节省一步,就像下面这样。

办法 2

$aref = [ 1, "foo", undef, 13 ];
# $aref 现在就是这个数组的引用了 如果没有这种语法,你就得这么来创建了 # @arr = [ 1, "foo", undef, 13 ]; $aref = @arr;
$href = { APR => 4, AUG => 8 };
# $href 就是这个哈希的引用了

办法2和办法1创建出来的引用是一样一样的,只是办法2节省一次不必要的变量名创建:

# 这样:
$aref = [ 1, 2, 3 ];
# 和这样是一样的:
@array = (1, 2, 3);
$aref = \@array;

如果你直接用[]或是{},那么你得到的就是一个全新的,空的数组或是哈希的引用了。

使用引用

现在你已经创建了引用,接下来就是在学习两种使用它的的方法。

使用方法 1

对于数组引用,你把引用名置于一对花括号里,然后就和普通的数组一样去操作就行了。比如用@{$aref}来代替@array来使用。

下面是例子:

数组:

1 @a;
2 @{$aref}; #同上一行的代码效果相同,$afref 是 @a 的引用,下面的同理
3 reverse @a; 
4 reverse @{$aref}; #Reverse the array
5 $a[3];
6 ${$aref}[3]; #An element of the array
7 $a[3] = 17;
8 ${$aref}[3] = 17; #Assigning an element

以上的代码,偶数行就是引用的用法啦。

关于哈希的用法,其实和数组是一样的:

1 %h; #$href是 %h 的引用
2 %{$href}; #A hash
3 keys %h;
4 keys %{$href}; #Get the keys from the hash
5 $h{'red'};
6 ${$href}{'red'}; #An element of the hash
7 $h{'red'} = 17;
8 ${$href}{'red'} = 17; #Assigning an element

在循环里的用法也是一样的:

for my $element (@{$aref}) {
...
}

循环打印引用哈希里的所有键值对:

for my $key (keys %{$href}) {
    print "$key => ${$href}{$key}\n";
}

使用方法 2

方法1已经够用了,但是你不觉得就是用起来有点麻烦吗,写出来的代码看起来很不美观啊,一堆花括号什么的。所以我们有方法2来拯救你。

${$aref}[3];
$aref->[3];#比上面这个美观多了吧

${$href}{red};
$href->{red};#方法2一点也不2

一个例子

有如下的代码,定义了一个二维数组

@a = ( 
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
);

那么$a[1]就是一个引用,它引用了一个一维数组,也就是[4,5,6]。记住下标从0开始。

那么$a[1]->[2]就是6。

箭头可选

$a[1]->[2] 看起来并不完美,其实可以把中间的箭头省略,变成这样$a[1][2] 是不是感觉好多了。

假设箭头并不是可选的,那么在某些情况就比较恶心了。比如你碰到了个二维以上的情况。

解决办法

关于前面那个一个国家包含多个城市名的问题,现在我们用引用来看看。

 1 my %table;
 2 while (<>) {
 3   chomp;
 4   my ($city, $country) = split /,/;
 5   $table{$country} = [] unless exists $table{$country};
 6   push @{$table{$country}}, $city;
 7 }
 8 
 9 foreach $country (sort keys %table) {
10   print "$country: ";
11   my @cities = @{$table{$country}};
12   print join ', ', sort @cities;
13   print ".\n";
14 }

结尾

  • 你可以对任何东西使用引用,包括标量,函数,或是其他引用(既可以创建引用的引用)
  • 在使用方法1中,你其实是可以忽略那些花括号的,但前提是原本花括号要括住的东西就是标量。比如@$aref和@{$aref}是一样的,$$aref[1]和${$aref}[1]是一样的,但我们建议初学者还是不要装逼,老老实实加上花括号,或是使用方法2,即用箭头 ->
  • 像下面这样做并不会真正的拷贝数组里的内容:

    1. $aref2 = $aref1;

    你得到了两个指向相同数组的引用罢了. 如果你修改了 $aref1->[23],然后你输出 $aref2->[23] 看一看,你发现是一样的.

    如果你想要拷贝数组,请这么做

    1. $aref2 = [@{$aref1}];

    这个其实用了[...]这个方法来创建了一个新的匿名数组,然后把这个新的数组的引用赋给了$aref2,新数组的内容就是拷贝来自用$aref1所指向的数组的数据。

    同样的,拷贝哈希就是这样

    1. $href2 = {%{$href1}};
  • 如果要判定一个变量里到底包不包含引用,可以用 ref 这个函数。这个函数的返回值为真,即代表有引用。实际上这个函数在检测到变量里包含哈希的时候就会返回HASH,包含数组的时候就会返回ARRAY,这两个返回值都是真。

  • 如果你把引用当成是字符串来输出的话,就会得到如下信息

    1. ARRAY(0x80f5dec) or HASH(0x826afc0)

    如果你看到输出的东西是这个,你就知道你不小心打印了一个引用。你可以使用 eq 来判断两个引用是否相等,但还是建议用 == ,因为这个要快多了。

  • 你其实是可以把任意字符串来当成引用名来使用的,什么意思呢,看例子,其实也就是动态的引用名。这个叫软引用或是符号引用,这个是默认就开启的,要关闭的话就得在程序开头添加 use strrict 'refs'

    1 $foo = 100;         #$foo初始化为100
    2 $name = "foo";
    3 $$name = 1;       # $foo现在是1了

感谢

原文作者: Mark Jason Dominus

原文地址:http://perldoc.perl.org/perlreftut.html

原文地址:https://www.cnblogs.com/tuwenmin/p/2986425.html