当传递数组或者哈希表时,它将复制到@_中,这对于单个数组没有问题,但哈希表将被展开为键/值组合的列表。在子程序中将@_赋值给哈希表是可行的,通过初始化的方法:通过将键/值组合表赋值给它。下面例子想子程序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之间的差别: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");
}