子程序(Subroutines)
Perl子例程或函数是一组一起执行任务的语句。 您可以将代码分成单独的子例程。 如何在不同的子程序之间划分代码取决于你,但从逻辑上讲,除法通常是每个函数执行特定的任务。
Perl可互换地使用术语子例程,方法和函数。
定义并调用子程序
Perl编程语言中子例程定义的一般形式如下 -
sub subroutine_name {
body of the subroutine
}
调用Perl子例程的典型方法如下 -
subroutine_name( list of arguments );
在5.0之前的Perl版本中,调用子例程的语法略有不同,如下所示。 这仍然适用于最新版本的Perl,但不推荐使用它,因为它绕过了子程序原型。
&subroutine_name( list of arguments );
让我们看看下面的例子,它定义了一个简单的函数,然后调用它。 因为Perl在执行程序之前编译程序,所以在哪里声明子程序并不重要。
#!/usr/bin/perl
# Function definition
sub Hello {
print "Hello, World!\n";
}
# Function call
Hello();
执行上述程序时,会产生以下结果 -
Hello, World!
将参数传递给子例程
您可以像使用任何其他编程语言一样将各种参数传递给子例程,并且可以使用特殊数组@_在函数内部访问它们。 因此函数的第一个参数是$ _ [0],第二个参数是$ _ [1],依此类推。
您可以将数组和散列作为参数传递,就像任何标量一样,但是传递多个数组或散列通常会导致它们失去单独的标识。 因此,我们将使用引用(在下一章中解释)来传递任何数组或哈希。
让我们尝试以下示例,该示例获取数字列表然后打印其平均值 -
#!/usr/bin/perl
# Function definition
sub Average {
# get total number of arguments passed.
$n = scalar(@_);
$sum = 0;
foreach $item (@_) {
$sum += $item;
}
$average = $sum/$n;
print "Average for the given numbers : $average\n";
}
# Function call
Average(10, 20, 30);
执行上述程序时,会产生以下结果 -
Average for the given numbers : 20
将列表传递给子例程
因为@_变量是一个数组,所以它可以用来为子程序提供列表。 但是,由于Perl接受并解析列表和数组的方式,从@_中提取单个元素可能很困难。 如果你必须传递一个列表和其他标量参数,那么将list作为最后一个参数,如下所示 -
#!/usr/bin/perl
# Function definition
sub PrintList {
my @list = @_;
print "Given list is @list\n";
}
$a = 10;
@b = (1, 2, 3, 4);
# Function call with list parameter
PrintList($a, @b);
执行上述程序时,会产生以下结果 -
Given list is 10 1 2 3 4
将哈希传递给子例程
当您向接受列表的子例程或运算符提供哈希时,哈希会自动转换为键/值对列表。 例如 -
#!/usr/bin/perl
# Function definition
sub PrintHash {
my (%hash) = @_;
foreach my $key ( keys %hash ) {
my $value = $hash{$key};
print "$key : $value\n";
}
}
%hash = ('name' => 'Tom', 'age' => 19);
# Function call with hash parameter
PrintHash(%hash);
执行上述程序时,会产生以下结果 -
name : Tom
age : 19
从子程序返回值
您可以像使用任何其他编程语言一样从子例程返回值。 如果未从子例程返回值,则在子例程中最后执行的任何计算自动也是返回值。
您可以像任何标量一样从子例程返回数组和哈希值,但返回多个数组或哈希值通常会导致它们失去单独的标识。 因此,我们将使用引用(在下一章中解释)从函数返回任何数组或散列。
让我们尝试以下示例,该示例获取数字列表然后返回其平均值 -
#!/usr/bin/perl
# Function definition
sub Average {
# get total number of arguments passed.
$n = scalar(@_);
$sum = 0;
foreach $item (@_) {
$sum += $item;
}
$average = $sum/$n;
return $average;
}
# Function call
$num = Average(10, 20, 30);
print "Average for the given numbers : $num\n";
执行上述程序时,会产生以下结果 -
Average for the given numbers : 20
子程序中的私有变量
默认情况下,Perl中的所有变量都是全局变量,这意味着可以从程序中的任何位置访问它们。 但您可以随时使用my运算符创建称为lexical variables private变量。
my运算符将变量限制在可以使用和访问它的特定代码区域。 在该区域之外,不能使用或访问此变量。 该区域称为其范围。 词法范围通常是一个带有一组括号的代码块,例如定义子例程主体的那些或标记if, while, for, foreach,和eval语句的代码块的代码块。
以下是一个示例,说明如何使用my运算符定义单个或多个私有变量 -
sub somefunc {
my $variable; # $variable is invisible outside somefunc()
my ($another, @an_array, %a_hash); # declaring many variables at once
}
让我们检查以下示例以区分全局变量和私有变量 -
#!/usr/bin/perl
# Global variable
$string = "Hello, World!";
# Function definition
sub PrintHello {
# Private variable for PrintHello function
my $string;
$string = "Hello, Perl!";
print "Inside the function $string\n";
}
# Function call
PrintHello();
print "Outside the function $string\n";
执行上述程序时,会产生以下结果 -
Inside the function Hello, Perl!
Outside the function Hello, World!
Temporary Values via local()
当变量的当前值必须对被调用的子例程可见时,主要使用local 。 本地只为全局(含义包)变量提供临时值。 这称为dynamic scoping 。 词法范围是用我的,它更像C的自动声明。
如果为local提供了多个变量或表达式,则必须将它们放在括号中。 此运算符的作用是将这些变量的当前值保存在隐藏堆栈的参数列表中,并在退出块,子例程或eval时恢复它们。
让我们检查以下示例以区分全局变量和局部变量 -
#!/usr/bin/perl
# Global variable
$string = "Hello, World!";
sub PrintHello {
# Private variable for PrintHello function
local $string;
$string = "Hello, Perl!";
PrintMe();
print "Inside the function PrintHello $string\n";
}
sub PrintMe {
print "Inside the function PrintMe $string\n";
}
# Function call
PrintHello();
print "Outside the function $string\n";
执行上述程序时,会产生以下结果 -
Inside the function PrintMe Hello, Perl!
Inside the function PrintHello Hello, Perl!
Outside the function Hello, World!
State Variables via state()
还有另一种类型的词法变量,它们类似于私有变量,但它们保持状态,并且在多次调用子例程时不会重新初始化。 这些变量使用state运算符定义,可从Perl 5.9.4开始提供。
让我们检查以下示例来演示state变量的使用 -
#!/usr/bin/perl
use feature 'state';
sub PrintCount {
state $count = 0; # initial value
print "Value of counter is $count\n";
$count++;
}
for (1..5) {
PrintCount();
}
执行上述程序时,会产生以下结果 -
Value of counter is 0
Value of counter is 1
Value of counter is 2
Value of counter is 3
Value of counter is 4
在Perl 5.10之前,你必须像这样写 -
#!/usr/bin/perl
{
my $count = 0; # initial value
sub PrintCount {
print "Value of counter is $count\n";
$count++;
}
}
for (1..5) {
PrintCount();
}
子程序调用上下文
子例程或语句的上下文被定义为期望的返回值的类型。 这允许您使用单个函数,该函数根据用户期望接收的内容返回不同的值。 例如,以下localtime()在标量上下文中调用时返回一个字符串,但在列表上下文中调用它时返回一个列表。
my $datestring = localtime( time );
在此示例中,$ timestr的值现在是由当前日期和时间组成的字符串,例如,Thu Nov 30 15:21:33 2000.相反 -
($sec,$min,$hour,$mday,$mon, $year,$wday,$yday,$isdst) = localtime(time);
现在,各个变量包含localtime()子例程返回的相应值。