Perl----子程序练习

来源:互联网 发布:网络舆情公司 编辑:程序博客网 时间:2024/06/07 13:49
#!/usr/bin/perl -w#创建子程序检查$values的值# sub printOK# {# if($values>10){# print "Values is $values.\n";# }# else {# print "Value is too small.\n";# }# }# $values=15;# printOK;#在默认情况下Perl变量在全局范围内有效,因此可以在子程序printOK内部变量引用变量$values。变量的范围指可以引用变量的程序代码部分。#使用关键字local和my关键字来创建变量,这种变量的范围完全限于类似printOK这样的子程序内部。这种局部变量的范围完全限于子程序,这意味着#它们可以和全局变量具有相同的名称,但根本不会影响全局变量的值。# sub printOK# {# my $localvalues=$values;# if($localvalues>10){# print "Values is $values.\n";# }# else {# print "Value is too small.\n";# }# }# $values=15;# printOK;# $values=8;# printOK ($values);#向子程序传递值时,那些值存储在特殊的perl数组@_中,可以在子程序内部访问这个数组。下面例子使用shift函数从@_数组得到传递给printOK的值:sub printOK{my $localvalues=shift(@_);if($localvalues>10){print "Values is $values.\n";}else {print "Value is too small.\n";}}$values=15;printOK($values);#使用函数时必须使用这种方式$values=8;printOK ($values);#返回值#除了接收传递的值之外,子程序也可以返回值,下面的例子使用return函数从子程序返回传递的两个值之和#从子程序返回值取代了子程序本身的名称,而且在print语句中直接连接在字符串内:sub addem{($value1,$value2)=@_;return $value1+$value2;}print "2+2=".addem(2,2)."\n";#perl和其他语言不一样,在perl中,不需要再使用子程序之前的声明,除非在没有将参数包围在括号的情况下使用子程序,这中情况下,必须在使用之前声明或者定义子程序。#perl中声明子程序的不同方法:#sub SUBNAME;#sub SUBNAME(PROTOTYPE);#sub SUBNAME BLOCK;#sub SUBNAME(PROTOTYPE) BLOCK;#从perl程序包中导入子程序:#use packagename qw(subname1 subname2 subname3);#子程序也有前缀反引用符号,使用反引用符号是可选的。子程序的反引用符号是:&,出现在每个子程序名称开头的&字符是名称的隐含组成部分,可以忽略它。#如果希望在addem的时候不添加括号:方法一是在使用addem之前声明sub addem;$value3=addem 2,2;print "2+2=$value3\n";sub addem{($value1,$value2)=@_;return $value1+$value2;}#方法二:在使用子程序之前定义子程序:sub addem{($value1,$value2)=@_;return $value1+$value2;}print "2+2=".addem 2,2;print "\n";#使用子程序原型#-----------------------------------------------------------------------------------------------------------------------------------------#        声明             |               原型#    sub SUBNAME($)       |         SUBNAME $argument1;                                    #    sub SUBNAME($$)      |         SUBNAME $argument1,$argument2;   #    sub SUBNAME($$;$)    |         SUBNAME $argument1,$argument2,$optionalargument;   #    sub SUBNAME(@)       |         SUBNAME $arrayargment1,$arrayargment2,$arrayargment3;   #    sub SUBNAME($@)      |         SUBNAME $argument1,$arrayargment1,$arrayargment2;   #    sub SUBNAME(\@)      |         SUBNAME $argument1;   #    sub SUBNAME(\%)      |         SUBNAME %{$hashreference};   #    sub SUBNAME(&)       |         SUBNAME anonymoussubroutine;   #    sub SUBNAME(*)       |         SUBNAME *argument;   #    sub SUBNAME()        |         SUBNAME ;   #定义子程序#sub SUBNAME BLOCK#sub SUBNAME (PROTOTYPE) BLOCK;sub Fprinthello {print "Hello!\n";}Fprinthello;#调用printhello函数打印helloprinthello();#声明子程序sub printhello {print "Hello!\n";}#调用子程序#perl中调用子程序的方法:#1.使用子程序前缀反引用符号&:&subname(argumentlist)#2.使用括号,&是可选的:subname(argumentlist)#3.预先声明子程序,或者从程序包中导入子程序,或者在代码中已经定义子程序,则可以忽略括号:subname argumentlist;#4.将子程序名称存储在数量中,并称之为&$scalar:# $scalar=subname;# &$scalar(argumentlist);#使用不同的方法调用addem函数:$value=&addem(2,3);print "Use &addem(2,3) :$value\n";$value=addem(2,4);print "Use addem(2,4):$value\n";$value=addem 2,3;print "Use addem  2,3:$value\n";$name="addem";$value=&$name(2,5);print "use \&\$name(2,5):$value\n ";#调用之前检查子程序是否存在:#在调用子程序之前,可以通过内置的perl的defined函数来检查它是否存在:@_=(2,8);$value=&addem if defined addem;#用defined函数判定是否存在addem函数,如果存在将addem计算的结果值赋给$valueprint "for \@\_=(2,8) in (\$value=\&addem if defined addem)=$value\n";#perl v5.6.0版本,可以使用exists来检查是否定义了子程序:@_=(2,10);$value=&addem if exists &addem;#用exists函数判定是否存在addem函数,如果存在将addem计算的结果值赋给$valueprint "for \@\_=(2,10) in (\$value=\&addem if exists \&addem)=$value\n";#如果编写的代码将被其他人复制到他们的应用程序中,在调用子程序之前检查是否定义了子程序是非常用用的#读取传递给子程序的参数:#1.可以在子程序中的代码中使用特殊数组@_读取传递给子程序的参数。这个数组专门用于保存传递给子程序的参数。#2.如果像子程序传递了两个传递了两个参数,则子程序的代码可以用$_[0]和$_[1]的形式来得到参数的值。#用子程序addem加两个数字,并打印结果,用addem(2,6)的形式来使用这个子程序。sub addem1{  $value1=$_[0];  $value2=$_[1];  print "\$value1+\$value2=".($value1+$value2)."\n";}addem1(2,6);#从@_中获取值的方法和使用数组的方法一样。#用shift函数从@_中获取值:sub addem2 {$value1=shift @_;$value2=shift @_;print "Use \shift \@\_ \$value1+\$value2=".($value1+$value2)."\n";}addem2(10,5);#子程序中默认shift使用@_,上方代码可以这样:sub addem3 {$value1=shift;$value2=shift;print "Use \shift in \$value1+\$value2=".($value1+$value2)."\n";}addem3(10,8);#使用列表赋值来一次得到@_中的所有值:# sub addem4 # {# ($value3,$value4)=@_;# print "Use\(\$value3,\$value4\)=\@\_ in \$value3+\$value4=".($value3+$value4)."\n";# }# addem4(10,10);#PS:当向子程序传递标量时,它们按引用传递,实际传递了标量值的引用(标量引用的作用类似存储在内存中的标量地址)。当修改传递的值时,也修改了传递这个值的最初代码的那个值。#增加传递给子程序addone的值(传递的值¥value将增加):sub addone{ ++@_[0];}$value=1;addone($value);print "Use \+\+\@\_\[0] in addone(\$value):$value\n";#增加数组中每个元素的值,并返回增加值以后的数组:sub addone_1{foreach $_(@_){      $_++;}return @_;}@b=(1,2,3,4,5,6,7,8,9,10);@b_addone=addone_1(@b);print  join ("  ,",@b_addone);print "\n";#当传递数组或者哈希表时,它将复制到@_中,这对于单个数组没有问题,但哈希表将被展开为键/值组合的列表。#在子程序中将@_赋值给哈希表是可行的,通过初始化的方法:通过将键/值组合表赋值给它.#下面例子想子程序printem传递给了哈希表,这个子程序将打印哈希表的所有元素:$hash{fruit}=peach;$hash{vegetable}=broccoli;$hash{pie}=blueberry;sub printem{$hash=@_;foreach $key(keys %hash){print "$key=>$hash{$key}\n";}}printem(%hash);#PS:因为传递给子程序的参数会展开到一个平面列表中,如果传递了两个或则跟多的数组或哈希表,则那些数组或者哈希表中的元素将会构成@_中长长列表。#PS:为了在传递数组或者哈希表的时候保持各自的完整性,要按引用传递给他们。(按引用传递 按引用返回)#使用不同个数的参数#参数是在@_数组中传递的,perl可以很轻松的向子程序传递个数不同的参数。#为了确定传递了多少个参数,仅需检查数组长度$#_(PS,$#_是@_中最后一个元素的下标值,所以$#加1就得到了基于0的数组@_中的元素总数)。sub addem_4{print "You passed  ".($#_+1)."  element.\n";foreach $element(@_){$sum+=$element;}print join("  ,",@_)."=$sum\n";}addem_4(2,3,4);#为参数设置默认值:#||=运算符为用户可能忽略的参数提供默认值:sub addem_5{($value1,$value2)=@_;$value2  ||=1;print "\$value1+\$value2=". ($value1+$value2) ."\n";}addem_5(5);#列表赋值使变量¥value2为零时,为变量提供了默认值1。在传递5这个值时的到5+1=6;#PS:假设用户不会传递值0或者空字符串。更好的方法是使用defined函数:sub addem_6{($value1,$value2)=@_;if(!defined($value2)){$value2 =1;};print "\$value1+\$value2=". ($value1+$value2) ."\n";}addem_6(6);#在某些情况下,可选择参数处于参数列表末尾,可以明确检查@_中元素的个数,也就是$#_+1,以了解用户传递了多少个参数:sub addem_7{$value1==shift @_;if($#_>0){$value2 =@_[1];}else{$value2=1;};print "\$value1+\$value2=". ($value1+$value2) ."\n";}addem_7(7);#子程序的返回值就是计算的最后一个表达式,或者可以用return语句明确退出子程序,并指定返回值。返回值在适当的上下文中计算,这取决于子程序调用的上下文。#------------------------------------------------Demo-----------------------------------------------#返回哈希表sub gethash{$hash{fruit}=peach;$hash{fruit1}=peach1;$hash{fruit2}=peach2;$hash{fruit3}=peach3;$hash{fruit4}=peach4;return %hash;}%myhash=gethash;foreach $key(keys %myhash){              print "$key=>$myhash{$key}\n";}#返回undef指出操作失败#编写一个函数getdata,它从文件中读取数据。然而,如果在文件中读取数据时出现了问题,getdata将返回undef。#PS:这个例子使用eval函数来捕获错误,所以它不会对程序造成致命影响,而且可以通过检查$@a中的值,而了解是否出现了错误,eval将所有的错误都放在$@中:# sub getdata# {# open FILEHANDLE ,"<nonexist.dat>";# $line=<FILEHANDLE> if FILEHANDLE;# }# if($@){# return;# }else{# return $line;# }# $data=getdata();# if(defined($data))# {# print $data;# }else{# print "Sorry,getdata failed!\n";# }#用my设置范围:#默认情况下,perl中的变量是全局性的。#使用my关键字可以将变量限制在封闭的程序块、条件、循环、子程序、eval语句、或者用do、require、use包含的文件中。#my声明的变量具有“词汇意义的”范围,local声明的变量具有动态范围。#my与local的主要差异在:具有动态范围的变量在变量范围内调用的子程序是可以看见的。而具有词汇意义范围的变量并不是这样。#使用子程序的结果就是用my声明的变量将完全局限于子程序内使用。#为了用my声明数量值,以限制标量值的范围,需使用my $scalar。如果用my列出了多个元素,则列表必须出现在括号中。和my一起使用的所有元素都必须是合法的左值。#PS:只有数字字母标识符可以具有词汇范围,诸如$_这样的特殊内置元素必须使用local声明。#----------------------------------------------------------------DEMO-----------------------------------------------------------------------sub printhem{my $inner=shift @_;print $inner;}printhem "Hello\n";#print $inner;#PS:my仅适用于标量值、数组和哈希表# my $variable;# my ($variable1,$variable2);# my  $variable=5;# my @array=(1,2,3);# my %hash;#声明为词汇范围的变量并没有限制在代码块中,相关的控制表达式也词汇范围的组成部分。#要求词汇范围的变量#把所用的全部变量都作为词汇范围的变量,可以使用附注:use strict 'vars'。这样,将从那个点开始到封闭程序块或者范围末尾之间对变量的任何引用,都必须引用专门声明的词汇变量,或者必须用它的包名称来限定。#local创建临时变量:#local创建动态范围变量,建立全局变量的临时副本,并使用这个临时副本,直至超出范围(那时,将恢复全局变量的值)#必须使用local的地方:创建类似$_特殊变量的局部副本、修改数组或哈希表中的一个元素、局部使用文件句柄和perl格式。#PS:local并不会创建变量,只创建全局变量的副本(而且保留全局变量的值,以供本地副本超出范围时候恢复)#使用local将建立所列变量的副本,并使它们局限于封闭的程序块、eval语句、do语句、从那个块内调用的任何子程序。#-----------------------------------------------------Demo-----------------------------------------------------------sub printifok{ local $local_value=$V_value; if($local_value>10){print "V_value is $V_value.\n"; }else{print "V_value is too small.\n"; }}$V_value=10;printifok;$V_value=12;printifok;#确定my和local之间的差别#PS:my创建了一个新变量,local将保存现存变量的副本。#local是运行期间的结构,而不是编译期间的结构,用local对全局变量你的改动在通过子程序调用离开局部范围时任然有效。#通常情况下,应该使用my,而不是local。my的执行速度快,而且没有全局副作用。然而,注意,必须使勇local来局部化任何以$开头的特殊变量。#用my声明的变量存储在专用符号表中,而不是作为整个包的符号表的组成部分。#用our设置变量:处理全局变量#our声明方法声明了变量在封闭的程序块、文件或者eval语句内是合法的全局变量。#our不会创建任何局部变量。#----------------------------------------------------Demo-----------------------------------------------# package package1;# $value_1=5;# package package2;# print "\$value_1 =" .$value_1;package package1;our $value_1;$value_1=5;package package2;print "\$value_1 =" .$value_1."\n";#创建永久(静态)变量:#有时候希望让子程序中的变量在两次调用子程序之间保留它的值,然而,如果用my或者local在子程序中声明变量,这些变量将在每次进入子程序的时候重置。#--------------------------------------Demo--------------------------------------# sub incrementcount# {# my $count;# return ++$count;# }# print incrementcount ."\n";# print incrementcount ."\n";# print incrementcount ."\n";# print incrementcount ."\n";#如果$count成为静态变量可以解决这个问题,但perl不直接支持静态变量,默认情况下,全局变量是静态的,但用my声明的子程序变量不是静态的。#词汇变量只要处于范围之内就不会重置。#将my声明放在子程序外进行,然后将代码(声明和子程序)都放在括号内,这使得它的层次和对子程序的调用的层次相同:{my $count;sub incrementcount{return ++$count;}}print incrementcount ."\n";print incrementcount ."\n";print incrementcount ."\n";print incrementcount ."\n";#也可以将所有内容放在BEGIN块中,这个块将在程序加载的时候运行:# sub BEGIN# {# my $count;# sub incrementcount# {# return ++$count;# }# print incrementcount ."\n";# print incrementcount ."\n";# print incrementcount ."\n";# print incrementcount ."\n";# }#得到子程序的名称和caller:#caller函数返回当前子程序上下文的信息。一般情况下,可以这样使用caller:#   caller   EXPR# caller#在标量上下文中,如果在子程序使用,则返回主调代码包的名称,或者返回eval或者requore,否则返回不确定的值。#在表上下文中,这个函数像这样返回列表:# ($package,$filename,$line)=caller;#如果包含了EXPR,则caller返回其他信息,调试人员可以用这些信息打印堆栈跟踪记录。EXPR的值说明在当前堆栈架之前有多少堆栈框架需要返回(也就是子程序调用)。#   ($package,$filename,$line,$subroutine,$hasargs,$wantarray,$evaltext,$is_require)=caller($s);#----------------------------------------------------Demo--------------------------------------# sub addem_1# {# ($value_1,$value_2) = @_;# $value_1+$value_2;# print join("  ,",caller);# }# $value_3=addem_1(2,2);#递归调用子程序:sub factorial{my $value=shift (@_);return $value== 1? $value:$value * factorial ($value-1);}$result_1=factorial(6);print $result_1."\n";#在递归的情况下,需要my来局部化变量#嵌套子程序:#perl支持嵌套子程序,可以在子程序的内部嵌套子程序。#----------------------------------Demo----------------------------------------sub outer{my $s="Inside the inner subroutine.\n";sub inner{my $s2=$s;print $s2;}inner();}outer();#内联函数:如果函数的原型是(),既没有任何参数,则该函数在perl便以其中就是内联的。这样的函数必须由常量或者词汇范围内的标量构成。不能用&或者do引用。#覆盖内置子程序#当覆盖子程序时,就为它提供了一个新定义。可以覆盖子程序,包括内置在perl中的函数,但仅能覆盖从模块中导入的子程序。#可以使用subs附注用导入语法预先声明子程序,可以使用那些名称来覆盖内置函数。#---------------------------覆盖内置子程序例子-----------------------------------------# sub subs 'exit';# sub exit# {# print "Do you really want to exit?";# $answer= <>;# if($answer =~ m/^y/i){# CORE::exit;# }# }# while(1){# exit;# };#重新定义子程序:{#----------------------创建子程序sub_1-----------------------my $text=shift;sub sub_1{$text=shift;print "$text there!\n";}#----------------------创建子程序sub_2-----------------------sub sub_2{$text=shift;print "$text everyone!\n";}#-----调用sub_1,重新定义它,使得它引用sub_2,并再次调用它-------sub_1("Hello");*sub_1=\&sub_2;sub_1("Hello");}


0 0