《高级Perl编程》第一版,俗称黑豹书!
come on ,baby !
一.数据引用与匿名存储引用本身就是一种标量变量.引用打印出来的值为所引用值的类型和内存地址.
$rarray=\@array;
print $rarray; #打印结果为类似于 SCALAR(0xb20c)
引用方式$ra=\$a; #标量的引用
$rarray=\@array; #数组的引用
$rhash=\%hash; #散列表的引用
优先级$$rarray[1] 等价于 ${$rarray}[1]
Perl解析此类表达式遵循两个规则:
(1)键值和索引定位被放到最后.
(2)离变量名最近的前缀最先联编.
箭头记号$$rarray[1] 等价于 $rarray->[1]
$$rhash{'a'} 等价于 $rhash->{'a'}
注意;这种记号仅对单键值的索引起作用.对于分段存取不行.
$rarrapackegey->[1,2]同$rarray->[2],Perl将括号中的内容视为逗号分隔的表达式,该表达式返回最后一个元素.
两个下标间的箭头可以省略.
$rarray->[1]->[1] 等价于 $rarray->[1][1]
匿名存储的引用布尔值
匿名存储即为不使用变量名动态为数据分配存储空间.
匿名存储的引用即为动态分配的存储空间的引用,指向这个内存地址.
$ra=[] #创建匿名数组,返回引用.
$rh={} #创建匿名散列表,返回引用.
ref函数 如果标量为引用,返回代表所指数据类型的字符串( SCALAR,HASH,ARRAY.REF,GLOB,CODE,'packge name')
如果标量不是引用,返回false(布尔值).
符号引用
$x=10;
$var='x';
$$var=20; # $x将被修改为20.因为$var是一个符号引用.
当对 $$var 进行访问时,首先检查 $var 是否为引用,在这里不是,而是字符串,Perl将再给表达式一次机会,作为符号引用.
一般来说都应该禁止符号引用.
use strict 'ref'; #不允许符号引用.
no strict 'ref'; #允许符号引用.
内部工作细节变量在逻辑上表示为名字与数值之间的联编.(全局变量.词法变量,动态作用域变量均如此),名字与数值是分开独立的.
每个数值保留一个引用计数,表示指向该数值的名字的个数.
当变量超出作用域时,名字与值的连接将删除,引用数递减,Perl将自动删除引用记号为0的值.
这和Unix文件系统中的硬连接类似.当创建对文件的硬连接时,该文件引用计数递增.除非引用计数递减为0,否则该文件不会被真正删除.
e.g.
@array=("hello");
$ra=\$array[0];
pop @array;
print $$ra; #打印 "hello"
该例中,虽然数组清空,没有元素,但"hello"这个数值的引用不为0,该数值没有被删除,所以通过指向这个数值的名字可以打印"hello".
可以这样想:'hello'为一个文件,$array[0]和$$ra都为文件的硬连接,当$array[0]删除后,文件'hello'的连接数不为0,不会被删除,通过$$a硬连接即可访问该文件.
注;名字与数值是相互独立的.删除名字只是删除了名字与数值之间的关联,而不是删除了数值.数值的删除是Perl根据引用数的值为零而自动完成.
二.实现复杂的数据结构三.Typeglob和符号表对于全局变量,Perl使用符号表把标识符映射到相应的值,在符号表与其他数据结构之间存在一个typeglob的数据结构,typeglob是真实的数据结构,拥有前缀"*".
词法变量词法变量即为以my限定的变量,不存在于符号表中.在每个代码块或子例程中都有一个或一串变量数组,被称为便签薄(scratchpad),每个词法变量都是这个数组中占据一项.词法变量即为局部变量.
动态作用域以local操作符修饰的全局变量,在申明的代码块中先将原来变量的值保存,然后可以进行赋值,在代码块结束时回复原来值.
local与my的比较(1)使用local不创建新的变量,除了对代码块外,对代码块中调用的过程也同样可见.
(2)使用my新创建一个变量,对代码块可见,对代码块中调用的过程不可见.
Typeglob通过Typeglob赋值可以创建标识符的别名.
e.g.
$spud='Wow';
@spud=('idaho','russet');
*potato=*spud;
进行typeglob赋值后,所有名为spud的实体都可以使用potato来指代,两个名字可以互换.符号表中的两个条目指向相同的typeglob值.
Typeglob与引用Typeglob和引用均指向值.变量$a可以简单的视为typeglob的简介访问${*a},两种表达式 ${\$a} ${*a} 指向同一个标量.
(1)别名的选择性
*b=\$a #将一个标量的引用赋值给typeglob
Perl将只安排$b与$a互为别名,而@a与@b等其他的则不能.
此即为别名的选择性,通过赋值相应的引用于typeglob而达到别名的选择性.
(2)常量
通过对常量的引用创建只读变量.
*PI=\3.1415926;
在此,$PI为只读变量,不能对其修改.
(3)为匿名子程序起名
$rs=sub {}; #$rs为匿名子程序的引用
*greet=$rs; #别名的选择性
greet(); #等价于 &$rs()
文件句柄.目录句柄与打印格式open(F,"/home/tmp");
opendir(D,"home");
F,D即为句柄.
对于open.read.write和readdir等内建函数,Perl自动传递符号的typeglob.而不是句柄字符串.
注:所有接收文件句柄的IO操作符也接收typeglob的引用 . F 同于 \*FIO重定向open(F,"/tmp/x") or die;
*STDOUT=*F;
print "hello world\n";
STDOUT以别名指向F,print函数默认向STDOUT输出信息,在此即定向到了F.
间接文件句柄 ( Indirect Filehandles )open ( $my,"/tmp/");
(1)假如$my已经定义,$my将被当成符号引用,用$my字符串作为句柄名,用 use strcit 'refs' 禁止符号引用.
(2)假如$my未定义,Perl将自动生成一个匿名句柄,并把该句柄的引用赋值给$my,把该引用作为open的参数,此时的$my即为间接文件句柄..
间接文件句柄的好处:因为句柄是全局的,如果在一个循环里使用 open(F,"/tmp") 等类似的程序将冲突.而使用 open (my $my."/tmp") 将会每次调用生成一个新的匿名文件句柄,而不会冲突,且离开程序块后,$my将失效,匿名文件句柄的引用数为0,将自动删除.
详见 perldoc -f open or perldoc perlopentut
四.子例程引用与闭包
子例程引用(1)有名子例程引用
sub greet {}
$rs=\&greet;
注:创建子例程引用是不要使用圆括号. $rs=\&greet() 表示把函数返回值的引用.
(2)匿名子例程引用
$rs=sub {}; #此为表达式,最后必须加分号.
使用子例程引用调度表
典型的调度表就是一个包含子例程引用的数组.
%option = (
'-h' => \&help,
'-f' =>sub { },
'-default' => \&default,
);
信号处理程序
Perl提供一个%SIG的特殊变量,key为信号名,对应的value为相应的子例程引用,根据相应的信号调用相应的子例程.
sub ctrl_c {
print " \n";
}
$SIG{"INT"}=\&ctrl_c;
通过修改,当按下CTRL_C时,将打印字符串 "CTRL_C pressed" .
闭包闭包为一种特殊的匿名子例程,它保留自己被创建时所在作用域中要使用的值.(只针对词法变量 my )
e.g.
sub greet {
my $str="hello world\n";
return sub { print "$str";}
}
my $rs=&greet();
&$rs(); #打印结果为"hello world"
在匿名子例程中,保留了创建时所在域中需要的变量(即此处的$str).
闭包原理以上例子为例:当第一次创建 $str 时,Perl会将名字"str"与一个新分配的标量变量值联编,并把值的引用数设置为1,因为在随后的匿名子例程中使用了$str,变量值的引用数加1变为2,在greet子例程结束返回匿名子例程的引用后,greet子例程结束,而匿名子例程没有结束(返回了它的引用),所以变量值的引用数减1而不是减2,标量变量值的引用数为而1不会被释放,在匿名子例程调用这个值时还存在.
闭包的应用(1)智能回调
(2)迭代器
五.eval函数eval函数有两种截然不同的运行方式,分别为动态表达式计算和例外处理.
字符串形式:表达式计算在此方式下,eval将把字符串的内容当作小程序来运行.
e.g.
$str='$c=$a+$b';
$a=10;$b=20;
eval $str;
print $c; #打印30
eval函数将把字符串$str的内容当作程序来运行.
如果$str字符串包含无效的表达式,Perl将把错误放到$@的变量中;如果$str没有语法错误,$@将为undef.
代码块形式:例外处理eval后面跟一个代码块,将用来处理运行时的错误.错误可以内建的(内存移除,除数为零),或者是用户通过die自己定义的.
e.g.
eval {
$c=10/0;
}
print $@; 将打印出 'Illegal division by 0 at test.pl line 2'
注意你的引号$str='$c=10';
$s1=eval $str; # $s1=10
$s2=eval "str"; # $s2=10
$s3=eval '$str'; # $s3='$c=10'
$s4=eval ( $str }; # $s4='$c=10'
对于以上四种情况产生的结果.
(1).Perl将$str的内容提交给eval,把内容作为小程序来执行.
(2).Perl提交给eval之前,先对双引号内的变量进行替换,然后eval把字符串"$c=10"当成程序运行,结果与1同.
(3).字符串由单引号括起来,变量替换时不会被展开.eval仅把'$str'当成程序来运行.eval返回最后一个表达式的结果,因此返回$str的值(即字符串'$c=10').
(4).与3结果相同.但在编译时就要对代码进行语法检查.
替换中的表达式计算s/regex/replacement/e
常规的正则替换模式直接把匹配的地方替换为replacement,但加入/e标志后,将把replacement当成一个Perl表达式,用表达式的结果去替换匹配的值.
$line="1+2=";
$line=~s/(/d)\+(/d)=/$1+$2/e;
print $line; #将打印"1+2=3"
/e标志使 $1+$2 作为了Perl表达式,运行结果为3,然后替换匹配值.
待续