加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

perl读书笔记

发布时间:2020-12-16 00:22:31 所属栏目:大数据 来源:网络整理
导读:1.在Perl 内部没有整数值,程序中的整数被当做等价的浮点数来处理。 2.Perl 允许用下划线来分隔整数,如:61298040283768==61_298_040_283_768;适用于非十进制数字表示中的整数部分,如16进制:0x5065727C==0x50_65_72_7C。 3.八进制以0 开头,十六进制以0x

1.在Perl 内部没有整数值,程序中的整数被当做等价的浮点数来处理。 2.Perl 允许用下划线来分隔整数,如:61298040283768==61_298_040_283_768;适用于非十进制数字表示中的整数部分,如16进制:0x5065727C==0x50_65_72_7C。 3.八进制以0 开头,十六进制以0x 开头,二进制0b 开头。 4.“前置0”指示符只对数字有效,对由字符串转换过来得数字无效。 5.10.5%3.2会转换为10%3后再计算。 6.**是指数操作符,如2**3,表示2 的3 次方,等于8。 7.最短的字符串不含任何字符。最长的字符串,可以填满整个内存。 在perl语言中,几个特殊而常用的符号: q? 是单引号 qq 是双引号 qw 单词列表引号 qr 正则表达式引号 qx 反引号 其中qq,qw又较为常用。 qq{foobar}的意思为意思为双引号字符串,可内插变量,相当于 "foobar" qw{foo bar}的意思为用空格分解字符串,得到列表,相当于如下语句 split(' ','foo bar') 得到的结果是'foo','bar' q 和 qq 运算符的特点: q 和 qq 必须是一个标识符,而不是标识符的部分。例如: q (abc)????? 用 () 作为分界符 q(abc)?????? 用 () 作为分界符 q xabcx????? 用 x 作为分界符 都是合法的, 而qxabcx 就会被当作是一个标识符来处理,有谁会想到 qxabcx 居然是一个 q 运算式呢? q 和 qq 后面的空格并不会影响语法,但是任何非空字符(不是空格、不是 TAB 字符、不是换行符)则会被当作界限符来使用。 如果 q 和 qq 使用一些特殊的符号的时候, 则必须配对。比如:< 只能和 >; 在一起用来当作界限符,而不能只用两个 < 作为界限符。 而 | 则只能和它自己作为一对界限符。这些特殊的符号有:()、{}、[]、<>; 8.Perl 中NULL字符没有特殊的含义。Perl 能计算长度,不用靠null 来判断字符串是否结束。 9.单引号字符串中,除了单引号和反斜线之外的任何字符都表示它自身。只有在反斜线()后面接的是或单引号’,其才会被当作特殊符号来处理。 10.字符串重复操作符(string repetition operator),由小写的字母x 表示,能把操作符左边字符串重复操作符右边数字那么多次。复制次数(右操作数)在使用之前会把它转换为小于等于它的整数(如,4.8 变为4)。重复次数小于1 将产生空串(长度为0)。 11.操作符(如+)需要数字,Perl 将把操作数当作数字看待。如果操作符需要字符串(如. ),Perl 将把操作数当作字符串看待。 12.若操作符(如+)需要数字,而给出的是个字符串,则字符串后面的非数字部分和前面的空格将被去掉;在极端情形,当一个不含任何数字的字符串将别转换为0。 13.当程序中包含可能的错误时,可以要求Perl 警告你。运行程序时,可以在命令行中使用–w 这个参数把警告打开,如下: $ perl–w my_program 或者: #! /usr/bin/perl –w 14.Perl5.6 或之后的版本中,可以使用pragma 来打开警告(warning),如: #! /usr/bin/perl use warnings; 15.在字符串中变量前($符号前)加上反斜线(),变量将不会被内插(替换)。 16.在双引号字符串中,可以用花括号将变量名括起来,以便于区分,如: print “fred ate $n $whats.n”; #是使用变量$whats的值 print “fred ate $n ${what}s.n”; #是使用变量$what的值 17.if-else分支中,花括号是必须的(这一点和C 不同)。 18.Perl没有Boolean 类型。perl的真假判断规则: *如果值为数字,0 是false;其余为真 *如果值为字符串,则空串(‘’)为false;其余为真 *如果值的类型既不是数字又不是字符串,则将其转换为数字或字符串后再利用上述规则 *undef为false,所有的引用都是true。 *字符串‘0’是唯一一个非空但值为0 的串。 19.如果字符串结尾有换行符,chomp可以去掉它。如: chomp($text); #去掉换行符(n)。 @lines = <STDIN>; #读入所有的行 chomp = (@lines); #去掉所有的换行符 或: chomp (@lines = <STDIN>); #读入所有的行,不包括换行符 20.可以使用defined 函数判断变量值,为undef 时返回false,其余返回true。 21. 当数组变量出现在预期简单变量出现的地方,则PERL解释器取其长度。 @array = (1,2,3); $scalar = @array; #$scalar=3,即@array的长度 ($scalar) = @array; # $scalar=1,即@array第一个元素的值 注:以数组的长度为循环次数可如下编程: $count = 1; while ($count <= @array) { ? print ("element $count: $array[$count-1]n"); ? $count++; } 22.(5..1)#空列表—..中的左值应小于右值,否则为空。 23.$end = $#rocks; #数组最后一个元素的索引 $number_of_rocks = $end + 1;#数组长度 24.数组的负数索引值从最后一个元素开始。 25.以下数组形式是等价的: (“fred”,“barney”,“betty”,“wilma”,“dino”) qw(fred barney betty wilma dino ) qw ! fred barney betty wilma dino ! qw# fred barney betty wilma dino # #有些像注释 qw( fred barney betty wilma dino ) qw{ fred barney betty wilma dino } qw[ fred barney betty wilma dino ] qw< fred barney betty wilma dino > qw(fred barney betty wilma dino) 26.数组名前加@(后没有中括号)来引用整个数组。$用于引用单个数组元素。 27.perl中数组只能包含标量值。 28.@copy = @quarry; #将一个数组中的值拷贝的另一个数组中,即改变一个数组的元素不会影响另一个数组。 29. @array = 5..9; $fred = pop(@array); #$fred 得到9,@array 现在为(5,6,7,8) $barney = pop @array; #$barney gets 8,@array 现在为(5,6,7) pop @array; #@array 现在为(5,6)(7 被丢弃了) 如果数组为空,那pop 什么也不做(因为没有元素可以移出),并返回undef。 30. push(@array,0); #@array 现在为(5,0) push @array,8; #@array 现在为(5,6,0,8) push @array,1..10; #@array 现在多了10 个元素 @others =qw/9 0 2 1 0 /; push @array,@others; #@array 现在又多了5 个元素(共有19 个) 31. @array = qw# dino fred barney #; $m = shift (@array); #$m 得到“dino”,@array 现在为(“fred”,“barney”) $n = shift @array; #$n 得到”fred”,@array 现在为(“barney”) shift @array; #@array 现在为空 $o = shift @array; #$o 得到undef,@arry 仍为空 unshift(@array,5); #@array 现在为(5) unshift @array,4; #@array 现在为(4,5) @others = 1..3; unshift @array,@others; #array 现在为(1,3,4,5) 32.数组也可以插入双引号的字符串中,插入的数组元素会自动由空格分开。 需要注意字符串中的邮件地址: $email =“fred@bedrock.edu”; #错误!将会替换@bedrock $email =“fred@bedrock.edu”; #正确 $email =‘fred@bedrock.edu’; #另一种方法 33.Perl 最常用的默认变量:$_,如: foreach(1..10){ #使用默认的变量$_ print “I can count to $_!n”; } 34.reverse(逆转)操作将输入的一串列表(可能是数组)按相反的顺序返回。 @fred = 6 ..10; @barney = reverse (@fred); #得到10,9,8,7,6 @wilma = reverse 6 ..10; #同上,没有使用额外的数组 @fred = reverse @fred; #将逆转过的字符串存回去 注意reverse 返回逆转的列表,它不会改变其参数的值。如果返回值没有赋值给某个变量,那这个操作是没有什么意义的: reverse @fred; #错误,没有改变@fred 的值 @fred = reverse @fred; #改变了@fred 的值 35.sort: @rocks = qw/ bedrock slate rubble granite /; @sorted = sort(@rocks); #得到bedrock,granite,rubble,slate @back = reverse sort @rocks; #为slate 到bedrock @rocks = sort @rocks; #将排序的值写回@rocks @numbers = sort 97 ..102; #得到100,101,102,97,98,99 注意,同reverse一样,sort 返回逆转的列表,它不会改变其参数的值。 sort @rocks; #错误,不会修改@rocks @rocks = sort @rocks; #现在@rocks 值是经过排序的 36.Perl 中的表达式将根据其context 返回适当的值,如: @people = qw( fred barney betty ); @sorted = sort @people; #列表context:barney,betty,fred $number = 42 + @people; #标量context:42+3,得到45 @list = @people; #3 个People 的列表 $n = @people ; #数字3 37. 一些表达式根本没有标量context 的值。例如,sort 在标量context 中返回什么?你不需要要排序一个列表来得到其个数,因 此,除非有人按另一种方式实现了sort,否则其在标量context 中返回undef。 另一个例子是reverse。在列表context 中,它返回反转的列表。在标量context 中,返回反转的字符串(或者将反转的结果串 成一个字符串): @backwards = reverse qw / yabba dabba doo /; #返回doo,dabba,yabba $backwards = reverse qw/ yabba dabba doo /; #返回oodabbadabbay 38. 其用法是显然的:如果一个表达式不是列表值,则标量值自动转换为一个元素的列表: @fred = 6*7; @barney = “hello”. ‘’. “world”; 下面是另一个例子: @wilma = undef; #OOPS!得到一个元素的列表(undef),不同于下面的例子 @betty = (); #将数组置空的正确方法 39.Perl 子程序可以带参数,Perl 会自动将此参数列表(此参数列表的另一个名字)存放在一个叫做@_的数组中。@_是子程序的一个私有变量。 40.如果没有使用括号,my 仅定义一个变量。如: my $fred,$barney; #错误!没有定义$barney my ($fred,$barney); #两个均定义了 41.use strict; #迫使perl编译时采用更严格的检测。 42.许多人推荐如果程序长度大于一个屏幕,则需要使用use strict。 43.标量并非子程序返回的唯一类型。如果你在列表context 中调用某个子程序,则其会返回列表值。 44.读文件简写: foreach(<STDIN>){#列表上下文,一次性读入,内存消耗大 print “I saw $_”; } while (defined($_ = <STDIN>)){#标量上下文,按行读入,内存消耗小 print “I saw $_”; } 45.命令行中的-代表标准输入,如调用命令为"$ ./my_program fred – betty",其含义是程序将首先处理文件fred,其次是标准输入流,最后是文件betty。 46.尖括号操作(<>)是一种特殊的行输入操作。从一个文件到另一个文件之间没有空行,当使用<>时,就像输入的是一个大文件一样。如果输入结束时,<>将返回undef(同时退出while 循环)。如: #$ ./my_program fred barney betty while(<>){ chomp; print “It was $_ that I saw!n”; } 47.<>从数组@ARGV 中得到调用参数。这个数组是Perl 中的一个特殊数组,其包含命令行参数的列表。程序开始运行时,调用参数已被存在@ARGV 之中了。 <>操作查看@argv 来决定使用哪些文件。如果表为空,则使用标准输入流; 在启动程序后,使用<>之前,可以手动修改@argv 的值,在用户没有输入必要参数时以作默认处理。 48.IO流,<或空表示只读,>写,>>追加。>会首先清空文件。 49.open打开文件时,成功返回真,打开失败返回假。 50.当结束一个文件句柄时,可以使用close关闭,关闭文件句柄将清空缓存,并释放文件的锁。如: close BEDROCK; 51.$!包含对系统请求失败时的异常信息。如下: if(!open LOG,“>>logfile”){ die “Cannot create logfile:$!”; } 52.程序的名字在Perl 的特殊变量$0。 53.die 函数可以指定某个严重错误,warn 函数来产生警告信息。 54.默认情况下,如果不指定文件句柄给print(或者printf,这里的内容对两者均适用),则默认会使用STDOUT。可以通过select 操作进行更改: select BEDROCK; print “I hope Mr. Slate doesn’t find out about this.n”; print “Wilma!n” $| = 1;#将变量$|设置为1,将会在输出操作结束时会立刻清空文件句柄。 select STDOUT;#将默认文件句柄恢复为STDOUT。 55. #将出错信息送到私有错误日志上 if(! Open STDERR,“>>/home/barney/.error_log”){ die “Can’t open error log for append: $!”; } 56.当在名字前面是美元符号($),后面是花括号({}),则其为hash 元素。 57.使用%引用整个哈希表;和数组一样,使用$引用单个变量。 58.hash 反转: %inverse_hash = reverse %any_hash;#value 和key 的位置交换了。 59.构造哈希表: my %last_name = ( “fred” => “flintstone”,“dino” => undef,“barney”=> “rubble”,“betty”=> “rubble” ); 60. keys 函数会返回此hash 的所有keys,values 函数将返回所有的values。如果hash 中没有元素,则此函数将返回空列表: my %hash = (“a”=>1,“b”=>2,“c”=>3); my @k = keys %hash; my@v = values %hash; 61.使用each遍历hash表: while (($key,$value) = each %hash){ print “$key => $valuen”; } 62.使用foreach遍历hash: foreach $key (sort keys %hash){ $value =$hass{$key}; print “$key => $valuen”; #print “$key => $hash{key}n”; } 63.使用exists 函数查看hash 中是否存在某个key。 if(exists $books{$dino}){ print “Hey,there’s a libaray card for dino!n”; } 64.delete 函数将某个给定的key(包括其对应的value)从hash 中删除。 my $person = “betty”; delete $books{$person}; #将$person 的借书卡删除掉 65.模式匹配中,匹配变量$n代表模式中第n对括号所匹配的字符串。匹配变量的值会保持不变,直到下一个模式成功匹配为止 $_ = “Hello there,neighbor”; if(/s(w+),/){ #空格和逗号之间的词 print “the word was $1n”; #the word was there } 66.自动匹配变量:$&,$`,$' $&为整个被匹配的部分,匹配部分的前一部分存放在$`之中,后一部分被存到$'。三个变量连在一起得到原始字符串。 67.绑定操作符=~,将右边的模式在左边的字符串上进行匹配。 68.use strict 'vars';以后,所有的变量在使用前必须先被定义。 69.使用system或exec函数时,最好使用参数列表的形式调用,以免被注入危险代码。如下: system "/bin/echo",$message; 70.perl汇报错误的特殊变量: $! : 从操作系统或库函数调用得到的错误。 $? : 最近一次调用wait()得到的返回值。 $@ : 最近一次调用eval()得到的错误。 $^E : 操作系统特有的错误信息。 80.caller()函数可以返回当前的调用栈,如: run(); sub run { ??? print caller();#输出:main/home/ubuntu/workspace/perl/test.pl3 } 81.[]用于取数组的引用(见下面95),{}用于取哈希的引用,如: #数组引用: $arr = ['a','b']; print $arr->[0];#a #哈希引用: $hash = { ??? 'a'=>'aaa',??? 'b'=>'bbb' }; print $hash->{'a'};#aaa 82.使用*取符号表,如下: $foo = "fdfdf"; whoami(*foo); sub whoami { local $glob = shift; print *{$glob}{PACKAGE},":",*{$glob}{NAME};#main:foo; } 83.使用grep过滤列表: my @results = grep EXPR,@input_list; my $count?? = grep EXPR,@input_list; 如: my @input_numbers = (1,8,16,32,64); my @bigger_than_10 = grep $_ > 10,@input_numbers; 84.使用map对列表进行转换: my @input_numbers = (1,64); my @result = map $_ + 100,@input_numbers; 85.使用eval包裹可能出异常的代码块,$@变量记录可能的异常信息: eval { $average = $total / $count } ;#<--注意分号 print "Continuing after error: $@" if $@; 86.eval是一个函数,因此可以有返回值,带代码块抛出异常时,返回值为undef,否则返回最后一个表达式的值: my $average = eval { $total / $count } ; 87.可以使用eval来执行动态代码,执行过程中发生的异常记录在$@变量中: eval '$sum = 2 + 2'; print "The sum is $sumn"; warn $@ if $@; 88.使用use命令引入模块,后面数组可限定引入的具体函数。 use File::Basename qw( fileparse basename ); #即使模块内只有一个函数,最好也写上qw(fileparse),以便统一。 #对于未显式引入的函数,可以使用全限定名引用(类似于java): my $dirname = File::Basename::dirname($some_path); #不带限定的use代表引入默认的函数列表(!注意不是全部,默认列表由模块的作者指定) use File::Basename; #带空列表的use代表不引入任何函数: use File::Basename ();#no import my $base = File::Basename::basename($some_path); #对于面向对象的模块,无需使用限定列表 use File::Spec; my $filespec = File::Spec->catfile( $homedir{gilligan},'web_docs','photos','USS_Minnow.gif' ); 89.PERL5LIB环境变量设定的目录会被加到perl的模块搜索目录列表: $ export PERL5LIB=/Users/home/Ginger 90.安装perl模块: $ perl Makefile.PL PREFIX=/Users/home/Ginger #或 $ perl Build.PL --install_base /Users/home/Ginger #然后: make make test make install 91.INC数组指定了模块搜索路径,use命令会参照此数组来搜索导入模块; use命令是在编译期执行,因此,运行期动态改变此数组并不能影响use操作,如下: unshift @INC,'/home/gilligan/lib';#broken,because 'unshift' happens at runtime use Navigation::SeatOfPants; #可以使用BEGIN代码块来修改@INC数组,因为BEGIN代码在编译期执行 BEGIN { unshift @INC,'/home/gilligan/lib'; } use Navigation::SeatOfPants; #或者使用lib pragma,同样在在编译期执行 use lib '/home/gilligan/lib'; use Navigation::SeatOfPants; #注意,use lib指定的是搜索目录,而不是具体的模块文件,因此下面的写法是错误的: use lib '/home/gilligan/lib/Navigation/SeatOfPants.pm'; # WRONG #编译期只会对变量进行声明,而不会赋值,因此下面的写法也是错误的 my $LIB_DIR = '/home/gilligan/lib'; use lib $LIB_DIR;???? # BROKEN use Navigation::SeatOfPants; #可以使用use constant在编译期声明一个常量来解决上面的问题: use constant LIB_DIR => '/home/gilligan/lib'; use lib LIB_DIR; use Navigation::SeatOfPants; #综上,BEGIN代码和use lib都会改变代码,而且跨平台时写死的路径也可能引起问题, #建议尽量使用PERL5LIB环境变量或perl -I参数来解决自定义搜索路径的问题 92.是取引用操作符 my $reference_to_skipper = @skipper; my $aa = shift @$reference_to_skipper;#取出数组中的第一个元素 my $bb = shift @{$reference_to_skipper};#取出数组中的第一个元素 #两种往函数中传递数组的方式: check_required_items('skipper',@skipper);#传递指针 check_required_items('skipper',@skipper);#拷贝数组 #下面三者代表同一个值,数组里的第二个元素: $skipper[1] ${$reference_to_skipper}[1] $$reference_to_skipper[1] #也可以使用->来解引(dereference),如下: $reference_to_skipper->[1] #可以合并连续的->操作符 my $root = @all_with_names; $root -> [2] -> [1] -> [0] $root -> [2][1][0] #注意,只有位于方括号或花括号(hash的情况)之间->操作符才能省略,即(if the arrow ends up between "subscripty kinds of things," #such as square brackets,we can also drop the arrow. ) #观察下面两组写法: $root->[2]->[1]->[0] $root->[2][1][0]#注意不是$root[2][1][0] # $all_with_names[2]->[1]->[0] $all_with_names[2][1][0] 93.对hash取引用: my %gilligan_info = ( ? name => 'Gilligan' ); my $hash_ref = %gilligan_info; my $name = $ gilligan_info { 'name' }; my $name = $ { $hash_ref } { 'name' }; my $name = $$hash_ref? { 'name' }; my $name = $hash_ref -> { 'name' }; #由hash引用转回 my @keys = keys %$hash_ref; #->操作符省略的规则同样适用于hash引用,下面两者是等同的 $crew[0]->{'name'} $crew[0]{'name'} 94.perl使用引用计数(reference counting)来跟踪对象的引用变化。 #只有引用计数降为0时,perl才会回收对象的内存 #对于回收的内存,perl通常不会返还给操作系统 #循环引用会破坏引用计数 #下面也会创建引用,函数退出时,该引用的随之失效,引用计数减一。 check_provisions_list(@skipper) #可以将引用赋值为undef或其他值来取消引用 $reference_to_skipper = undef; #块内部定义的引用,块退出时随之失效 my @skipper = ...; { # naked block my $ref = @skipper; ... } # $ref goes out of scope at this point 95.使用[]创建匿名数组的引用 my $ref_to_skipper_provisions = [ qw(blue_shirt hat jacket preserver sunscreen) ]; $aa = $ref_to_skipper_provisions->[1];#hat #上面等同于 my $ref_to_skipper_provisions; { ? my @temporary_name = ? ( qw(blue_shirt hat jacket preserver sunscreen) );#使用匿名数组构建新数组,匿名数组中的所有元素填充到新数组中。 ? $ref_to_skipper_provisions = @temporary_name; } #另一种常用的写法 my @skipper_with_name = ( ? 'The Skipper',? [ qw(blue_shirt hat jacket preserver sunscreen) ] ); #下面三种写法是等价的 my $fruits = [qw('pineapple','papaya','mango')]; my $fruits = ['pineapple','mango']; my $fruits; { ? my @secret_variable = ('pineapple','mango'); ? $fruits = @secret_variable; } #再来个复杂点的,返回一个大小为3的数组,每个数组元素是一个大小为2的数组(记为A)的引用, #A的第二个元素又是一个数组的引用。 sub get_provisions_list { ? return ( ??? ['The Skipper',?? [qw(blue_shirt hat jacket preserver sunscreen)] ],??? ['The Professor',[qw(sunscreen water_bottle slide_rule radio)? ] ],??? ['Gilligan',????? [qw(red_shirt hat lucky_socks water_bottle)?? ] ],? ); } #还有 ['Mrs. Howell',? [? ]#空数组的匿名引用 ],96.不要犯迷糊 #构建大小为2的数组,第二个数组元素是一个引用,指向另一个数组 my @skipper_with_name = ( ? 'The Skipper',? [ qw(blue_shirt hat jacket preserver sunscreen) ] ); #构建大小为6的数组 my @skipper_with_name = ( ? 'The Skipper',? qw(blue_shirt hat jacket preserver sunscreen) ); 97.使用{}创建匿名hash的引用 my $ref_to_gilligan_info = { ? name???? => 'Gilligan',? hat????? => 'White',? shirt??? => 'Red',? position => 'First Mate',}; my @crew = ( ? { ??? name???? => 'Gilligan',??? hat????? => 'White',??? shirt??? => 'Red',??? position => 'First Mate',? },? { ??? name???? => 'Skipper',??? hat????? => 'Black',??? shirt??? => 'Blue',??? position => 'Captain',#这个逗号是特意留的,美其名曰便于维护 ); #代码块和匿名hash都使用{},因此可能会使perl解析器判断错误, #此时,在{}前加个加号,则明确的告诉解析器这是一个匿名hash; #在{}内开始处加个分号,则明确的告诉解析器这是一个代码块; #如下: ?+{ ... }#匿名hash ?{; ... }#代码块 98.autovivification 任何一个不存在的或者是值为undef的变量,当被解引(dereference)以便从中寻找 一个位置(学术上被称为“左值上下文”)来存储某项内容时,perl会自动的 将其初始化并将一个匿名的空项目引用赋值给它,如下: my %provisions; push @{ $provisions{'aa'} },$1; #等价于: my %provisions; $provisions{'aa'} = [? ];#键aa指向一个匿名空数组 push @{ $provisions{'aa'} },$1; #下面的例子, my $top; $top->[2]->[4] = 'lee-lou'; #等同于: my $top = [undef,undef,[undef,'lee-lou']]; #下面两个是等价的 my %total_bytes1; $total_bytes1{'professor.hut'}{'gilligan.crew.hut'} += 1250; %total_bytes2 = ('professor.hut'=>{'gilligan.crew.hut'=>1250}); #print Dumper(%total_bytes1,%total_bytes2); 99.函数引用: sub add { ??? my ($a,$b) = @_; ??? $a + $b; } my $c = &;add; print &$c(1,2),"n"; print &{$c}(1,"n"; print $c->(1,"n"; 100.匿名函数: my $func_add = sub { ??? my ($a,$b) = @_; ??? $a + $b; }; my $c = $func_add->(1,2); print $c,"n"; #作为回调函数使用: use File::Find qw(find); my @starting_directories = qw(.); find( ? sub { ??? print "$File::Find::namen"; ? },? @starting_directories,); 101.perl中,一个函数可以访问其函数体外部定义的变量,那么这个函数就被称为闭包 #闭包举例1,计算某个目录下所有文件的大小之和: use File::Find qw(find); my @starting_directories = qw(/usr/bin/); my $total_size; find( ? sub { ??? $total_size += -s $File::Find::name; ? },); print $total_size,"n"; #闭包举例2,为文件夹下的每个文件编号: use File::Find; my $callback; { ? my $count = 0; ? $callback = sub { print ++$count,": $File::Find::namen" }; } #此处,虽然定义$count变量的代码块已经结束,已不能显式引用$count, #但由于其被callback引用,因此在callback内部$count作为匿名变量依然有效 find($callback,'.'); #闭包举例3,将闭包函数作为函数返回值返回: use File::Find; sub create_callback { ? my $count = 0; ? return sub { print ++$count,": $File::Find::namen" }; } my $callback = create_callback(? ); print "my bin:n"; find($callback,'/bin');#第一批调用 print "my etc:n"; find($callback,'/etc');#此处$callback内$count的值仍会保持$callback上次调用时的值 #闭包举例4,将闭包函数作为函数返回值返回: use File::Find; sub create_callback { ? my $count = 0; ? return sub { print ++$count,": $File::Find::namen" }; } my $callback1 = create_callback(); my $callback2 = create_callback(); print "my bin:n"; find($callback1,'/bin'); print "my etc:n"; find($callback2,'/etc');#$callback1与$callback2是两个函数实例,各自内部维护了一个$count匿名变量 #闭包举例5,获取闭包函数返回值: use File::Find; sub create_callback { ? my $total_size = 0; ? return sub { ??? if (@_) { # it's our dummy invocation ????? return $total_size; ??? } else { # it's a callback from File::Find: ????? $total_size += -s if -f; ??? } ? }; } my $callback = create_callback(? ); find($callback,'/bin'); my $total_size = $callback->('dummy'); # dummy parameter to get size print "total size of bin is $total_sizen"; #闭包举例6,获取闭包函数返回值: use File::Find; sub create_callbacks { ? my $total_size = 0; ? #两个回调函数访问同一个$total_size变量 ? return (sub { $total_size += -s if -f },sub { return $total_size }); } my ($count_em,$get_results) = create_callbacks(); find($count_em,'/bin'); my $total_size = &$get_results(); print "total size of bin is $total_sizen"; #闭包举例7: use File::Find; sub print_bigger_than { ? my $minimum_size = shift; ? return sub { print "$File::Find::namen" if -f and -s >= $minimum_size }; } my $bigger_than_1024 = print_bigger_than(1024); find($bigger_than_1024,'/bin'); #闭包举例8,非匿名的闭包: #为$count提供了两个访问函数,同时保护了$count不被任何其他代码块私自修改, #$count被叫做静态本地变量(static local variable) BEGIN {#编译期立即执行(类似于java中的static代码块) ? my $count; ? sub count_one { ++$count } ? sub count_so_far { return $count } } count_one(); count_one(); print count_so_far(),"n"; 102.my与local的区分 使用local修饰某个变量,是为了告诉perl,如果此变量名已经被使用, 则暂时保存那个值直至使用相同变量名的局部变量失效。 103.文件句柄引用 #使用变量存储文件句柄 open my $log_fh,'>>','castaways.log' ??????? or die "Could not open castaways.log: $!"; log_message( $log_fh,'My name is Mr. Ed' ); sub log_message { ? my $fh = shift; ? print $fh @_,"n"; } while( <$log_fh> ) { ... }#read something #当文件句柄引用变量失效(超出作用范围)时,perl会自动关闭文件 204.目录句柄引用: opendir my $dh,'.' or die "Could not open directory: $!"; foreach my $file? ( readdir( $dh ) ) { ??? print "Skipper,I found $file!n"; } ------------------------------------------------------------------------------ 104.use的特点 use FirstModule ; # 导入 FirstModule 模块 # use 导入模块文件后缀必须为.pm,但 require 可以使用其他后缀 use v5.6.0 ; # 比较当前perl的版本号,如果比指定的版本小,便会产生严重错误,而中止程序 use FirstModule 2.0; # 比较倒入模块的版本 如果导入的模块的版本过低,便会产生严重错误,而中止程序 our $VERSION=2.0; # 设定当前模块的版本号 use FirstModule qw( @array ); #只从模块中导入指定的 @array 数组 no # 不从指定的名称空间里导入指定项目 ,语法等价于 use use strict ;# 强迫声明时添加 作用域 全局或字典;强迫使用引号封闭字符串;强迫明确调用每个子程序。 use strict 'vars'; # 强迫声明时添加 作用域 全局或字典; use strict 'subs';# 强迫使用引号封闭字符串;强迫明确调用每个子程序。 use vars qw( 变量1 变量2 ); # 声明全局变量 use warnings; # 打印警告信息 no strict; # 关闭语法检查 use constant PI => 3.1415926; # 创建常量标示符 use diagnostics; #启用诊断效果 ? enable(); # 开启诊断 ? disable(); # 禁用诊断 use integer; # 采用整数运算方式 105.perl中可以使用use、do或require来包含文件,三者的区别参见百度。 106.perl中包和模块的使用和区别,参照百度。 107.our和my的区别,包变量和词法变量的区别,参照百度。 108.使用@ISA数组来指定一个类的父类: package Cow; use base qw(Animal);#Perl 5.005 or earlier #或 package Cow; use Animal; our @ISA = qw(Animal);#perl 5.6 or later #或 package Cow; use Animal; @Cow::ISA = qw(Animal); #或 package Cow; use Animal; use vars qw(@ISA); @ISA = qw(Animal); 109.perl中类也是使用包来组织,因此,对于类中的方法, 可以使用->调用,也可以使用::来调用,但是两种调用方式是有区别的: ->是使用类名来调用,实参列表与形参列表不一致,实参列表的第一个元素是类名,是由perl默认传入的; 而::是通过包名来调用,此时被调用的函数就是一个普通的(面向过程的)函数,实参列表与形参列表一致,perl不会默认往参数列表顶端插入类名。 最终,不建议使用::来调用类中的方法,这样容易引起混乱。 110.子类调用父类的方法: { package Animal; ? sub speak { ??? my $class = shift; ??? print "a $class goes ",$class->sound,"!n"; ? } } { package Mouse; ? use base qw(Animal); ? sub sound { 'squeak' } ? sub speak { ??? my $class = shift; ??? $class->SUPER::speak;#从@ISA中寻找第一个speak并调用 ??? print "[but you can barely hear it!]n"; ? } } Mouse->speak(); 111.调用类方法的方式: Class->method(@args); #或 my $beast = 'Class';#使用字符串调用,恶心的perl!!! $beast->method(@args); 112.在外部通过bless创建类实例: { package Mouse; ? sub speak { ??? my $class = shift; ??? print "squeakn"; ? } } my $name = 'Mr. Ed'; my $ins_mouse = $name; bless $ins_mouse,'Mouse'; $ins_mouse->speak;#调用类实例的方法 print $$ins_mouse;#解引 112.在内部通过bless创建类实例(终于走向正轨了!): { package Horse; ? sub sound { 'neigh' } ? sub getName { ??? my $self = shift; ??? $self->{name};#解引 ? } ? sub new { ??? my $class = shift; ??? my $name = shift; ??? bless {'name'=>$name},$class;#返回匿名类实例 ? } } my $tv_horse = Horse->new('Mr. Ed'); print $tv_horse->getName; 114.对于调用类方法, 通过类名调用时,实参列表第一个元素是类名(字符串); 通过类实例调用时,实参列表第一个元素是类实例(bless过的引用)。 115.使用ref函数,可以区分一个变量是不是引用,因此,可以使用他来区分类实例和类名。 sub name { ? my $either = shift; ? ref $either ??? ? $$either??????????????? # it's an instance,return name ??? : "an unnamed $either";?? # it's a class,return generic } 116.在函数中,可以使用$_[0]来引用第一个参数,以此类推。 这个可以用在类的set和get方法中: sub color???? { $_[0]->{Color} }#第一个元素的类实例 sub set_color { $_[0]->{Color} = $_[1] } 117.类中可以定义DESTROY函数,当类实例被销毁时,perl会自动调用该函数; 只有没有任何强引用(与弱引用相对)指向某个类实例时,perl才会回收类实例,调用DESTROY函数。 如果perl未在类定义层次中找到DESTROY函数,perl并不会报错。 如果类实例中引用其他的类实例,perl会由外向里依次调用DESTORY函数,即先销毁外层,再销毁内层。 118.重写DESTROY函数时,一定要调用父类的(SUPER::DESTROY),否则可能销毁不完全。 119.另一种类函数调用方式: my $obj = Some::Class->new(@constructor_params); #等价于: my $obj = new Some::Class @constructor_params; 120.perl中的弱引用(weak references): #弱引用不会增加引用计数 #对象被回收时,指向他的弱引用会被赋值为undef。 ## in Animal use Scalar::Util qw(weaken); # in 5.8 and later use WeakRef qw(weaken);????? # in 5.6 after CPAN installation our %REGISTRY;#记录每一个类实例 sub named { ? ref(my $class = shift) and croak 'class only'; ? my $name = shift; ? my $self = { Name => $name,Color => $class->default_color }; ? bless $self,$class; ? $REGISTRY{$self} = $self;#记录类实录,键是$self转化而成的唯一的字符串,值是类实例。 ? weaken($REGISTRY{$self});#变成弱引用,以便perl能够正常的回收。 ? $self; } 121.Perl 所有模块都隐含的继承了一个称作 UNIVERSAL的内建模块,并继承了如下三个方法: isa(包名) 例如:如果Rectangle模块继承了(无论是以间接的方法)Shape 模块,Rectangle->isa(‘Shape’) 将返回true can(函数名) 如果Rectangle 或它的任何基类包含有名为draw的函数,Rectangle->can(‘draw’)将返回true VERSION(版本号) 可以给UNIVERSAL添加新的方法。一旦给它添加了新的方法,所有的class都可以调用。 给UNIBERSAL添加新方法的例子: sub UNIVERSAL::log() { ??? my($self,$msg)=@_; ??? print "$self: $msgn"; } 122.调用某个对象的方法时,perl首先会在类层次间查找,如果没找到,会继续查找 UNIVERSAL基类;如果还未查找到,perl会重新在类层次及UNIVERSAL基类中查找AUTOLOAD函数, 如果找到,则会在原先的目标函数处直接调用AUTOLOAD函数,并传入参数列表,跟普通类方法一样, 实参列表第一个元素为类实例或类名,并将原先的目标函数的函数名赋值给$AUTOLOAD包变量。 123.可以在AUTOLOAD函数中定义目标子函数,以实现按需编译、安装的功能: ## in Animal sub AUTOLOAD { ? our $AUTOLOAD; ? (my $method = $AUTOLOAD) =~ s/.*:://s; # remove package name ? if ($method eq "eat") {## define eat: ??? eval q{ ????? sub eat { ??????? #... ??????? #long definition goes here ??????? #... ????? } ??? };??????????????? # End of eval's q{? } string ??? die $@ if $@;??????????????????????? # if typo snuck in ??? goto &eat;?????????????????????????? # jump into it ? } else {?????????????????????????????? # unknown method ??? croak "$_[0] does not know how to $methodn"; ? } } #如上,当第一次调用Animal::eat函数时,eat并不存在,因此进入AUTOLOAD,在AUTOLOAD中, 通过eval动态定义了eat,然后调用之;此后,当再次调用eat时,因为eat已被定义,就不会进入AUTOLOAD函数了。 124.使用AUTOLOAD简化get、set方法: sub AUTOLOAD { my @elements = qw(color age weight height); our $AUTOLOAD; if ($AUTOLOAD =~ /::get(w+)$/ and grep lcfirst($1) eq $_,@elements) { ? my $field = lcfirst $1; ? { ??? no strict 'refs'; ??? *{$AUTOLOAD} = sub { $_[0]->{$field} }; ? } ? goto &{$AUTOLOAD}; } elsif ($AUTOLOAD =~ /::set(w+)$/ and grep lcfirst($1) eq $_,@elements) { ? my $field = lcfirst $1; ? { ??? no strict 'refs'; ??? *{$AUTOLOAD} = sub { $_[0]->{$field} = $_[1] }; ? } ? goto &{$AUTOLOAD}; } else {?????????????????????????????? # unknown method ??? croak "$_[0] does not know how to $methodn"; } 125.引入模块时,下面两段代码是等价的: use Island::Plotting::Maps qw( load_map scale_map draw_map ); #等价于: BEGIN { ? require Island::Plotting::Maps; ? Island::Plotting::Maps->import( qw( load_map scale_map draw_map ) ); } 126.自定义模块时,可以继承Exporter模块,使用其定义的import方法来导出向调用者导出需要的函数, @EXPORT数组用于定义默认向外导出的函数列表,@EXPORT_OK用于定义其他的可以向外导出的函数列表。 如下: package Navigate::SeatOfPants; our @EXPORT = qw(guess_direction_toward); our @EXPORT_OK = qw(ask_the_skipper_about get_north_from_professor); use base qw(Exporter); #外界调用时如下: use Navigate::SeatOfPants;? # gets guess_direction_toward use Navigate::SeatOfPants qw(guess_direction_toward); # same use Navigate::SeatOfPants ? qw(guess_direction_toward ask_the_skipper_about); use Navigate::SeatOfPants ? qw(ask_the_skipper_about get_north_from_professor);## does NOT import guess_direction_toward! 127.%EXPORT_TAGS哈希表用于定义导出标签: package Navigate::SeatOfPants; use base qw(Exporter); our @EXPORT??? = qw(guess_direction_toward); our @EXPORT_OK = qw( ??????????????? get_north_from_professor ??????????????? according_to_GPS ??????????????? ask_the_skipper_about ??????????????? ); our %EXPORT_TAGS = ( ??????? all?????? => [ @EXPORT,@EXPORT_OK ],??????? gps?????? => [ qw( according_to_GPS ) ],??????? direction => [ qw( ??????????????? get_north_from_professor ??????????????? according_to_GPS ??????????????? guess_direction_toward ??????????????? ask_the_skipper_about ??????????????? ) ],??????? ); #外界调用时如下: use Navigate::SeatOfPants qw(:direction);

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读