子程序的定义

sub 子程序名{
子程序内容
}

子程序的调用

&子程序名

返回值

子程序中的最后一行即为返回值,如下示例:

#!/usr/bin/perl -w
$fred=5;
$barney=3;
sub sum_of_fred_and_barney{
print "Hey,you called the sum_of_fred_and_barney!\n";
$fred+$barney;    # 这就是返回值
}

$result=&sum_of_fred_and_barney;    # 这里的 $result 的值为15

但如果sum_of_fred_and_barney子程序修改为:

sub sum_of_fred_and_barney{
print "Hey,you called the sum_of_fred_and_barney!\n";
$fred+$barney;
print "Hey,I'm returning a value now!\n";
# 这里会给出结果1,也就是print函数执行成功之意
}

参数

在定义子程序的时候可以不用显示的指出参数个数和类型,但是在调用的时候就一定要给出参数列表。这点与C\C++语言就有些不同了
如下:

sub max{
if($_[0]>$_[1]){
$_[0];
} else {
$_[1];
}
}

上面这段代码中需要注意的是子程序的参数列表。默认情况下参数列表是属于 @_ 数组的,那么$_[0]就代表第一个元素也就是第一个参数,$_[1] 就是第二个参数。

$n = &max(4,5);

这时$n为5

那如果 $n = &max(4,5,6);

这样的话多余的参数会被忽略——反正子程序也不会用到$_[2],所以Perl并不在乎里面是否有值。参数如果不足也会被忽略——如果用到超出@_数组边界的参数,只会得到undef。

子程序中的私有变量

既然每次调用子程序是Perl都会给我们新的@_,难道不能让他产生私有变量吗?当然可以了!

默认情况下Perl中所有的变量都是全局变量。但如果在定义变量的前面使用my操作符的话,这个变量就变成了私有变量。
如下例子:

 sub max{
my ($m,$n)=@_;
if( $m>$n ){ $m; } else { $n; }
}

$m与$n的作用范围只在max子程序内,在程序其他部分的相应变量不会受到子程序变量的影响。反之,别的程序代码也无法访问或修改这些私有变量。

长度可变的参数列表

在真实的Perl代码中,尝尝把更长的(任意长度的)列表作为参数传给子程序。当然,子程序可以很容易的通过检查@_数组的长度来确定参数的个数是否正确。比方说我们可以将max函数完善一下检查参数列表:

 sub max{
if ( @_ !=2){
print "WARNING!&max should get exactly to arguments!\n";
}
my ($m,$n)=@_;
if( $m>$n ){ $m; } else { $n; }
}

更好的&max子程序

#!/usr/bin/perl -w

sub max{
my($max_so_far)=shift @_;    # 数组中的第一个值暂时把它当成最大值
foreach (@_){                # 遍历数组@_ 中的其他元素
if ( $_ > $max_so_far ){   # 判断当前的元素是否比$max_so_far还大
$max_so_far=$_;
}
}
$max_so_far;
}

$maximum = &max(3,4,5,6,7);

空参数列表

上文的&max函数即使超过2个参数,修改后的&max也可以应付,但如果一个参数都没有呢?这听起来似乎有些杞人忧天。毕竟,怎么可能会有人调用&max却不传入任何参数呢?但是,也许会有人写出如下的程序代码:

$maximum = &max(@numbers);

某些时候,数组@numbers或许是一个空的列表。也许数组中的内容是程序从文件里读入的,但文件却是空的。那么这种情况&max会怎么样呢?

子程序的第一行会对参数组@_(现在是空的)进行shift操作,以此作为$max_so_far的值。这并不会出错,因为数组是空的。所以shift会返回undef给$max_so_far

现在foreach循环要遍历@_数组,但是由于@_是空的,所以循环本身不会被执行。

接下来,perl将$max_so_far的值undef作为子程序的返回值。从某种角度来看,那是正确的结果,因为在空列表中没有最大的值。

关于语法(my)变量

事实上,词法变量可使用在任何语句块内,而不仅限于子程序的语句块。如if、while或foreach

每当我们谈到my()操作符是,请注意,在my不适用括号时,只用来声明单个词法变量如:
my $fred,$barney;        # 错!没声明 $barney
my ($fred,$barney);    # 两个都声明了

当然,也可以使用my来创建新的私有数组

my @phone_number;

所有新的变量的值一开始都是空的;标量被设为undef,数组被设为空列表。

use strict编译命令

所谓编译命令,就是对编译器的指示,告诉它关于程序代码的一些信息。这会让perl语法编译器强制执行一些严格的、确保良好程序设计的规则。

根据大部分人的建议,比整个屏幕长的程序都应该加上use strict

return 操作符

return操作符会从子程序中立即返回某个值:

#!/usr/bin/perl -w

use strict;

my @names = qw/zyq wcl zsc pf fyr zyz/;

sub which_element_is{
my($what,@array) = @_;
foreach (0..$#array){
if ( $what eq $array[$_] ){
return $_;
}
}

-1;
}

my $result = &which_element_is("wcl",@names);

print "$result\n";

输出结果为:
1

省略调用子程序时使用的&符号

有两种情况可以省略这个符号

情况1:调用子程序时使用的参数在括号内
my @cards = shuffle(@deck_of_cards);    # shuffle 前没有必要用&

情况2:子程序的定义在调用之前
sub division{
$_[0] / $_[1];
}

my $quotient = division 355,113;    # 这里甚至连括号都省略了。呵呵

特殊情况:当自己定义的子程序与Perl内置的函数同名时。如果少了&符号,就算我们已经定义了子程序&chomp,仍然会调用到内置函数chomp。

因此,真正的省略规则如下:

你知道所有Perl的内置函数名

非标量返回值

子程序不仅可以返回标量值,如果在列表上下文中调用它,他就能返回列表值。如下代码:

#!/usr/bin/perl -w

use strict;

my ($fred,$barney) = (11,6);

sub list_from_fred_to_barney{
if ($fred<$barney){
$fred..$barney;
} else {
reverse $barney..$fred;
}
}

my @c = &list_from_fred_to_barney;

print " @c\n"

输出结果:
11 10 9 8 7 6

持久性私有变量

说白了就是在子程序中的私有变量,但是这个私有变量在每次调用子程序时不会重新初始化。使用state操作符来定义持久性私有变量。

sub marine {
state $n = 0;  # private, persistent variable $n
$n += 1;
print "Hello, sailor number $n!\n";
}

----------本章结束----------