Perl - 子程序



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 接受和解析列表和数组的方式,因此很难从 @_ 中提取各个元素。如果您必须与其他标量参数一起传递列表,则将列表作为最后一个参数,如下所示:

#!/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 运算符创建称为 词法变量私有变量。

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!

通过 local() 获取临时值

local 主要用于当前变量的值必须对调用的子程序可见的情况。local 只是为全局(表示包)变量提供临时值。这称为动态作用域。词法作用域由 my 完成,其工作方式更类似于 C 的 auto 声明。

如果向 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() 获取状态变量

还有另一种类型的词法变量,它们类似于私有变量,但它们会维护其状态,并且在多次调用子程序时不会重新初始化。这些变量使用 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() 子程序返回的相应值。

广告