Like Share Discussion Bookmark Smile

J.J. Huang   2021-03-15   Perl   瀏覽次數:

Perl - 第十二章 | Perl 子程序(函數)

Perl子例程或函數是一起執行任務的一組語句。你可以將程式碼分成單獨的子例程。如何在不同的子例程之間劃分程式碼取決於你,但是從邏輯上來說,劃分通常是使每個函數執行特定任務。

定義並調用子程序

Perl編程語言中子例程定義的一般形式如下:

1
2
3
sub subroutine_name {
body of the subroutine
}

調用該Perl子例程的典型方式如下:

1
subroutine_name( list of arguments );

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

1
&subroutine_name( list of arguments );

讓我們看一下下面的範例,該範例定義一個簡單的函數,然後調用它。因為Perl在執行程序之前先對其進行編譯,所以在哪裡聲明子例程都沒有關係。

1
2
3
4
5
6
7
8
9
#!/usr/bin/perl

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

# Function call
Hello();

這將產生以下結果:

1
Hello, World!

向子程序傳遞參數

你可以像在任何其他編程語言中一樣將各種參數傳遞給子例程,並且可以使用特殊數組@_在函數內部訪問它們。因此,函數的第一個參數位於$ _ [0]中,第二個參數位於$ _ [1]中,依此類推。

你可以像任何標量一樣將數組和散列作為參數傳遞,但是傳遞多個數組或散列通常會導致它們失去各自的標識。因此,我們將使用引用(在下一章中進行說明)來傳遞任何數組或哈希。

讓我們嘗試以下範例,該範例獲取一個數字列表,然後輸出其平均值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/perl

# 定義求平均值函數
sub Average{
# 獲取所有傳入的參數
$n = scalar(@_);
$sum = 0;

foreach $item (@_){
$sum += $item;
}
$average = $sum / $n;
print '傳入的參數為 : ',"@_\n"; # 打印整個數組
print "第一個參數值為 : $_[0]\n"; # 打印第一個參數
print "傳入參數的平均值為 : $average\n"; # 打印平均值
}

# 調用函數
Average(10, 20, 30);

這將產生以下結果:

1
2
3
傳入的參數為 : 10 20 30
第一個參數值為 : 10
傳入參數的平均值為 : 20

向子程序傳遞列表

由於@_變量是一個數組,因此可以用來為子例程提供列表。但是,由於Perl接受和解析列表和數組的方式,可能很難從@_中提取單個元素。如果你必須將列表與其他標量參數一起傳遞,則將list作為最後一個參數,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/perl

# 定義函數
sub PrintList{
my @list = @_;
print "列表為 : @list\n";
}
$a = 10;
@b = (1, 2, 3, 4);

# 列表參數
PrintList($a, @b);

這將產生以下結果:

1
列表為 : 10 1 2 3 4

註:我們可以向子程序傳入多個數組和哈希,但是在傳入多個數組和哈希時,會導致丟失獨立的標識。所以我們需要使用引用(後面會學到)來傳遞。

向子程序傳遞哈希

當你向接受列表的子例程或運算符提供哈希值時,哈希值將自動轉換為鍵/值對列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/perl

# 方法定義
sub PrintHash{
my (%hash) = @_;

foreach my $key ( keys %hash ){
my $value = $hash{$key};
print "$key : $value\n";
}
}
%hash = ('name' => 'morose', 'age' => 3);

# 傳遞哈希
PrintHash(%hash);

這將產生以下結果:

1
2
age : 3
name : morose

子程序返回值

子程序可以向其他編程語言一樣使用 return 語句來返回函數值。
如果沒有使用 return 語句,則子程序的最後一行語句將作為返回值。

1
2
3
4
5
6
7
8
9
10
11
#!/usr/bin/perl

# 方法定義
sub add_a_b{
# 不使用 return
$_[0]+$_[1];

# 使用 return
# return $_[0]+$_[1];
}
print add_a_b(1, 2)

這將產生以下結果:

1
3

註:子程序中我們可以返回標量,數組和哈希,但是在返回多個數組和哈希時,會導致丟失獨立的標識。所以我們需要使用引用(後面會學到)來返回多個數組和函數。

子程序的私有變量

預設情況下,Perl中所有的變量都是全局變量,這就是說變量在程序的任何地方都可以調用。
如果我們需要設置私有變量,可以使用my操作符來設置。

my操作符用於建立詞法作用域變量,通過my建立的變量,存活於聲明開始的地方,直到閉合作用域的結尾。
閉合作用域指的可以是一對花括號中的區域,可以是一個文件,也可以是一個 if, while, for, foreach, eval字符串。

如何聲明一個或多個私有變量:

1
2
3
4
sub somefunc {
my $variable; # $variable 在方法 somefunc() 外不可見
my ($another, @an_array, %a_hash); # 同時聲明多個變量
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/perl
# 全局變量
$string = "Hello, World!";

# 函數定義
sub PrintHello{
# PrintHello 函數的私有變量
my $string;
$string = "Hello, Morosedog!";
print "函數內字符串:$string\n";
}
# 調用函數
PrintHello();
print "函數外字符串:$string\n";

這將產生以下結果:

1
2
函數內字符串:Hello, Morosedog!
函數外字符串:Hello, World!

變量的臨時賦值

我們可以使用local為全局變量提供臨時的值,在退出作用域後將原來的值還回去。

local定義的變量不存在於主程序中,但存在於該子程序和該子程序調用的子程序中。定義時可以給其賦值,如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/usr/bin/perl

# 全局變量
$string = "Hello, World!";

sub PrintMorosedog{
# PrintHello 函數私有變量
local $string;
$string = "Hello, Morosedog!";
# 子程序調用的子程序
PrintMe();
print "PrintMorosedog 函數內字符串值:$string\n";
}
sub PrintMe{
print "PrintMe 函數內字符串值:$string\n";
}

sub PrintHello{
print "PrintHello 函數內字符串值:$string\n";
}

# 函數調用
PrintMorosedog();
PrintHello();
print "函數外部字符串值:$string\n";

這將產生以下結果:

1
2
3
4
PrintMe 函數內字符串值:Hello, Morosedog!
PrintMorosedog 函數內字符串值:Hello, Morosedog!
PrintHello 函數內字符串值:Hello, World!
函數外部字符串值:Hello, World!

靜態變量

state操作符功能類似於C裡面的static修飾符,state關鍵字將局部變量變得持久。
state也是詞法變量,所以只在定義該變量的詞法作用域中有效,舉個例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#!/usr/bin/perl

use feature 'state';

sub PrintCount{
state $count = 0; # 初始化變量

print "counter 值為:$count\n";
$count++;
}

for (1..5){
PrintCount();
}

這將產生以下結果:

1
2
3
4
5
counter 值為:0
counter 值為:1
counter 值為:2
counter 值為:3
counter 值為:4

注1:state僅能建立閉合作用域為子程序內部的變量。
注2:state是從Perl 5.9.4開始引入的,所以使用前必須加上use。
注3:state可以聲明標量、數組、哈希。但在聲明數組和哈希時,不能對其初始化(至少Perl 5.14不支持)。

子程序調用上下文

子程序調用過程中,會根據上下文來返回不同類型的值,比如以下 localtime() 子程序,在標量上下文返回字符串,在列表上下文返回列表:

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/perl

# 標量上下文
my $datestring = localtime( time );
print $datestring;

print "\n";

# 列表上下文
($sec,$min,$hour,$mday,$mon, $year,$wday,$yday,$isdst) = localtime(time);
printf("%d-%d-%d %d:%d:%d",$year+1990,$mon+1,$mday,$hour,$min,$sec);

print "\n";

這將產生以下結果:

1
2
Mon Mar 15 10:08:01 2021
2111-3-15 10:8:1

註:以上參考了
Tutorialspoint, Perl - Subroutines