Effective Perl-chapter6

从perl5开始引入的引用是perl的学习中的一个重点,它为复杂数据结构和面向对象的编程的方式打开了一扇门。要想良好组织数据并对其进行整体传递,引用是关键,要想更好的解决复杂的问题,熟练的掌握引用是必须要攻克的。

理解引用和引用的语法

引用实际上是一个标量值,我们可以像数字和字符串一样,把引用直接存储于标量变量中或者作为元素存储到数组和哈希中。
我们可以把引用当成是指向其他perl对象的“指针”,引用可以指向任何类型的对象
在perl中只能对已经存在的对象创建引用

创建引用
最常用的是反斜杠操作符(或者说是“取引用”操作符)作用于一个变量

my $a = 3.1416;
my $scalar_ref = \$a;

反斜杠可以运用于任何变量名

my $array_ref = \@array;
my $hash_ref = \%hash;
my $sub = \⊂

对于数组和哈希元素也可以取引用

my $array_elem_ref = \$a[0];
my $hash_elem_ref = \$a{"hello"};

而对某个列表取引用的话,返回的是列表中每个元素的引用组成的新列表,而非列表本身引用,究竟返回何种数据的引用,得看取引用的方式

sub val {return 1 .. 3};

# 如果创建引用时用的是&符号,那么得到的是指向子程序val的代码引用
my $a = \(&val);

#如果创建引用前先调用val,那么返回的是标量引用
my $a = \(val() );        #得到的是val返回的列表中的最后一个值3的引用

#在列表上下文赋值
my ($a) = \(val() );      #得到的是val返回的列表中的第一个值1的引用

#上面是对从子程序返回的列表中创建引用的方式,对包含直接量的列表同样适用
my $a = \(1 .. 3);

我们经常用到匿名数组,匿名数组的构造语法和普通列表很像,唯一不同的是匿名数组用的是方括号

匿名数组会在内存中创建一个没有名字的数组,并返回该数组的引用,这是创建一列数据的引用最自然最常用的方法

my $a = [1 .. 3];
#现在$a就是一个数组引用,一个包含数字1,2,3的匿名数组

匿名哈希的构造是用花括号而不是方括号

my $h_ref = {'F' => 9, 'cl' => 17, 'br' => 35};

定义子程序时如果没有给出名字,则返回匿名子程序引用(代码引用)

my $greeting = sub {print "hello world\n"};
$greeting -> ();        #执行代码引用

在传递大字符串时,用标量引用就非常高效

my $string = 'a' x 100000;
some_sub {$string};    #将整个字符串复制了一遍
some_sub {\$string};   #传递字符串的引用

使用引用
解引用指的是取引用指向的那个值。最规范安全的语法是将引用放在代码块(花括号括起来的区块)中返回,然后把它当作某个变量或子程序的标识符使用

#标量解引用
my $a = 1;
my $s_ref = \$a;
print ${$s_ref};
${$s_ref} += 1;

#数组解引用
my @a = 1 ..5;
my $a_ref = \@a;
print "@{$a_ref}";
push @{$a_ref}, 6 .. 10;

如果引用值存在标量变量中,我们可以省略花括号,直接使用标量变量的名字,前面再加上$符号开头

my $string = "hello world";
my $ref = \$string;
print $$ref;

上面解引用的方式看起来不是很直观,可以使用箭头操作符,通过下标的方式取数组或哈希的值

${$h_ref}{'F'}        #常规方式
$$h_ref{'F'}            #简化方式
$h_ref -> {'F'}        #箭头方式

箭头可以连打,如果箭头左右两边都是元素下标的话,那就可以省略箭头

$a ->[1] = {'first' => 'joe',};
print $a -> [1] -> {'first'};
print $a -> [1]{'first'};      #可以省略箭头

自动代入
如果我们把某个含有未定义值的标量当作指向其他数据类型的引用,perl就会自动创建相应类型的数据,并把这个标量指向该数据类型的引用。这种方式称为Autovivification

my $ref;
$ref -> [3] = 'four';
#上述代码自动创建一个包含四个元素的数组,并将$ref作为指向该数组的引用

利用这个特性,我们可以免去逐层定义,非常方便地创建深层次的数据结构

my $ds;
$ds -> {top}[0]{cats}[1]{name} = 'Buster';

软引用
如果对一个字符串值解引用,perl会返回以字符串为名字的变量的值。如果变量不存在,就会自动创建,这个称为软引用

不要把普通标量当作引用使用,实际上创建了一个软引用

my $str = 'pi';
${$str} = 3.14;
print "$pi\n";        #通过软引用自动创建变量$pi

#我们可以直接用字符串创建软引用
${'e' . 'e'} = 2;
print $ee;

类似这样的变量名可以不是合法的标识符,我们可以创建以空白字符命名的变量(可能没什么用)

${} = 'space';

引用创建复杂数据结构

使用某个变量之前,确认它是常规数组还是数组引用

#一个数组,包含其他数组的引用
my @a = ( [1,2] , [3,4] );
print $a[0][1];        # 2

#指向一个数组的引用,该数组包含指向其他数组的引用
my $a = [ [1,2] , [3,4] ];
print $a -> [0][1];      # 2

#快速访问文本中的第几行
my @lines;
while (<>) {
        chomp;
        push @lines, [split];
}
my $thirld_on_seventh = $lines[6][2];        #第七行第三个单词

#统计某一行的单词数量
my $count = @{$lines[6]}        #统计第7行的单词数量

#统计最长单词数量的行数
my ($most_words) = 
map { $_ -> [0] } 
sort { $b -> [1] <=> $a -> [1] }
map { [ $_ , scalar @{ $lines[$_] } ] } 0 .. $#lines;

print "Line $most_words is the longest with" , "scalar @{ $lines[$most_words] }", "words\n"         

匿名数组操作符和列表操作符

匿名数组操作符[]和列表操作符()很像,表面上来看,它们都是为同一个目的服务的,那就是创建列表。但其实还有显著的不同之处

匿名数组返回的是一个引用,而不是列表,因此可以直接创建指向数组对象的引用,无需事先创建一个具名数组

my $aref = [ 0 .. 9 ];        # 列表上下文环境

通过匿名哈希创建C风格的struct结构

perl没有提供像C那样的stuct结构体类型的数据,一句话,perl其实是无结构的。但是,另一个角度看,哈希提供了极为相似的效果

$student{'last'} = 'smith';
$student{'first'} = 'John';
$student{'bday'} = '01/08/72';

当用到一个散列元素时,只要键名是合法的perl标识符,我们就可以省略它两边的引号

$student{last} = 'smith';

实际应用中,我们可以事先创建一个空结构,然后一点一点往里面填充

$student = {};
$student -> {last} = 'smith';

用map和grep操作复杂数据结构

用map切片
读入一个含有三维坐标的文件

# point data
1  2  3
4  5  6
7  8  9

open my ($points), '<' , 'points' or die "$!";

while (<$ponits>) {
        next if /^\s*#.*$/;
        push @xyz, [split];
}

foreach my $pt (@xyz) {
        print "point " ,"x = $pt -> [0] , y = $pt -> [1] , z = $pt -> [2]\n"
}

# 取出每个点的x轴值
for (my $i = 0 ; $i < @xyz ; $i ++) {
        push @x, $xyz[$i][0];
}
#用map最自然
my @x = map { $_ -> [0] } @xyz;  

用map嵌套数据

2019-06-25 10-10-37 的屏幕截图.png

把这些数据组合成三维数据结构

for (mt $i = 0 ; $i < @x ; $i ++) {
        $xyz[$i][0] = $x[$i];
        $xyz[$i][1] = $y[$i];
        $xyz[$i][2] = $z[$i];
}

#然而使用map,则更加简洁,直接在map里面用[]构造内层数据结构
my @xyz = map { [ $x[$_] , $y[$i] ,$z[$i] ] } 0 .. $#x;

#还可以交换x和y坐标的位置
my @yxz = map { [$_ -> [1] , $_ -> [0] , $_ -> [2] ] } @xyz;

#还可以利用切片对数组元素进行重新排列
my @yxz = map { @$_[1 , 0 , 2] } @xyz;

用grep进行选择

#收集y大于x坐标点的x坐标值
my @x = map { $_ -> [0] } grep { $_ -> [0] > $_ -> [1] } @xyz;

以上就是关于引用学习的记录

你可能感兴趣的:(Effective Perl-chapter6)