• 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

  • 相关阅读:
    R语言实战
    Python Google Translate API
    Windows使用技巧
    test_CSDN_markdown_format
    Linux: bash script
    test_markdown
    线性基学习笔记+模板总结
    Educational Codeforces Round 69 D Yet Another Subarray Problem
    图片托管
    二维线段树模板,建树,维护最大最小值
  • 原文地址:https://www.cnblogs.com/tuwenmin/p/2986425.html
Copyright © 2020-2023  润新知