Perl 简明教程

Perl - Subroutines

Perl 子例程或函数是一组共同执行任务的语句。您可以将代码划分为不同的子例程。如何将代码划分为不同的子例程取决于您,但通常从逻辑上讲,这种划分是让每个函数执行一个特定任务。

Perl 可以互换使用子例程、方法和函数这三个术语。

Define and Call a Subroutine

Perl 编程语言中子例程定义的一般形式如下 -

sub subroutine_name {
   body of the subroutine
}

呼叫 Perl 子例程的通常方式如下 −

subroutine_name( list of arguments );

版本的 Perl 在 5.0 之前,呼叫子例程的語法稍有不同,如下所示。這在最新版本的 Perl 中仍然有效,但不建議使用,因為它會略過子例程原型。

&subroutine_name( list of arguments );

讓我們來看看以下範例,它定義了一個簡單函式並呼叫它。因為 Perl 在執行程式之前會編譯您的程式,所以您在何處宣告您的子例程並不重要。

#!/usr/bin/perl

# Function definition
sub Hello {
   print "Hello, World!\n";
}

# Function call
Hello();

當以上程式執行時,會產生以下結果 −

Hello, World!

Passing Arguments to a Subroutine

您可以將各種參數傳遞給子例程就像在任何其他程式語言中一樣,並且可以在函式中使用特殊陣列 @ . Thus the first argument to the function is in $ [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

Passing Lists to Subroutines

因為 @_ 變數是一個陣列,所以它可用於對子例程提供清單。但是,由於 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

Passing Hashes to Subroutines

當您將雜湊提供給接受清單的子例程或運算子時,雜湊會自動轉換為一對關鍵字/值的清單。例如 −

#!/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

Returning Value from a Subroutine

您可以從子例程傳回值,就像在任何其他程式語言中一樣。如果您沒有從子例程傳回值,則子例程中最後執行的任何計算結果也會自動作為回傳值。

您可以從子例程傳回陣列和雜湊,就像傳回任何純量一樣,但通常傳回多個陣列或雜湊會導致它們失去各自的識別碼。因此,我們將使用參考(在下一章中說明)從函式傳回任何陣列或雜湊。

讓我們嘗試以下範例,它會取得數字清單然後傳回它們的平均值 −

#!/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

Private Variables in a Subroutine

預設情況下,Perl 中的所有變數都是全域變數,這表示可以在程式的任何地方存取這些變數。但是,您可以使用 my 運算子在任何時候建立稱為 lexical variablesprivate 變數。

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 通常用於必須讓呼叫的子例程可見變數的目前值。local 只會將暫時值提供給全域(表示封裝)變數。這稱為動態範圍。使用更類似於 C 的 auto 宣告的 my 來執行詞彙範圍。

如果將多個變數或表達式提供給 local,必須將它們放在括號中。這個運算子會儲存那些變數在隱藏佇列中參數清單的目前值,然後在離開區塊、子例程或 eval 時復原它們。

讓我們檢查以下範例,以區分全域和 local 變數 −

#!/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();
}

Subroutine Call Context

子例程或语句的上下文被定义为预期的返回值类型。这使您可以使用一个函数来返回不同的值,具体取决于用户期望接收的值。例如,以下 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() 子例程返回的相应值。