位元詩人 [Perl] 程式設計教學:隨語境 (Context) 改變程式的行為

Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email

前言

Perl 程式會根據程式執行時當下的語境來決定其行為,這算是一種 Perl 程式的內隱規則。在這些語境中,比較重要的是純量語境和串列語境,其他的稍微知道一下即可。

純量語境 (Scalar Context)

scalar 函式會把參數強制轉為純量語境,例如用 scalar 取得陣列長度:

my @arr = (1, 2, 3);

scalar(@arr) == 3 or die "Wrong length";

不僅透過 scalar 函式可取得純量語境,進行純量的操作也會轉為純量語境,如下例:

my @arr = (1, 2, 3);

@arr + 0 == 3 or die "Wrong length";

+ 0 會使 @arr 轉為純量語境,而陣列在純量語境下會回傳其長度。

指派到純量也會轉為純量語境,如下例:

my @arr = (1, 2, 3);
my $length = @arr;

$length == 3 or die "Wrong length";

同樣地,指派到純量時會強制轉為純量語境,這時 @arr 會回傳其長度。

串列語境 (List Context)

我們在操作串列或陣列時,會轉換至串列語境。像是先前提過的串列指派:

my ($a, $b, $c) = @arr;

如果我們把純量放到串列語境,會變成串列的第一個值,如下例:

my ($a, $b, $c) = 5;

$a == 5 or die "Wrong value";
!defined($b) or die "Wrong value";
!defined($c) or die "Wrong value";

這時候 5 轉變成單一元素的串列,$a 值為 5$b$c 則為未定義 undef

以下程式是串列語境而非純量語境:

my ($a) = (3, 4, 5);

$a == 3 or die "Wrong value";

這是因為 ($a) 視為單一元素的串列,並省略尾端的逗號。因此,$a 的值為 3。這算是 Perl 的一個小陷阱。

函式在不同語境的行為

我們在閱讀 Perl 文件時,會發現 Perl 文件在描述函式時,會分別介紹其在純量語境和串列語境的行為,以 reverse 函式為例 (參考這裡):

my @arr = reverse ("foo", "bar", "baz");
my $s = reverse ("foo", "bar", "baz");

scalar(@arr) == 3 or die "Wrong length";
$arr[0] eq "baz" or die "Wrong value";
$arr[1] eq "bar" or die "Wrong value";
$arr[2] eq "foo" or die "Wrong value";

$s eq "zabraboof" or die "Wrong value";

reverse 在串列語境時,會將元素反轉後回傳新的串列;在純量語境,則會將所有元素轉字串、合併、倒轉後回傳該字串。許多 Perl 的函式不論在純量語境或串列語境都能執行,但行為不同,故需注意閱讀文件。

撰寫函式時,要怎麼知道呼叫者的語境呢?Perl 提供 wantarray 函式來判斷 (參考這裡)。本系列文的目標是把 Perl 當成命令列工具,暫時不會深入撰寫函式的細節。

布林語境 (Boolean Context)

布林語境算是一種特殊的純量語境,雖然 Perl 沒有定義真偽值,但在內部會自動判斷。見下例:

if (()) {
    die "It should be empty";
}

在這個例子中,空串列 () 視為偽 (falsy),故不會拋出例外。

布林語境不需刻意學習,因為我們平常在寫判斷句 (conditional) 時,自然而然會用到布林語境。

虛無語境 (Void Context)

虛無語境算是一種特殊的純量語境,發生在程式拋棄回傳值時。例如,我們在以下程式中打開警告訊息:

use warnings;

"Hello World";

這時候程式會拋出以下訊息:

Useless use of a constant ("Hello World") in void context

這是因為我們忽略掉 "Hello World" 所引發的警告訊息。

刻意引發虛無語境對程式沒啥幫助,這通常是忘了處理回傳值所引發的語境。

如果我們想讓函式 func 以串列語境來表現其行為,但不想要其回傳值,可以指派到空串列:

() = func();

相對來說,不接收其回傳值直接執行則會變成虛無語境,如下例:

func();

字串安插語境 (Interpolative Context)

指的是在雙引號中自動解析變數的情形。見下例:

my $name = "Michelle";
my $greeting = "Hello, $name";

$greeting == "Hello Michelle" or die "Wrong value";
關於作者

身為資訊領域碩士,位元詩人 (ByteBard) 認為開發應用程式的目的是為社會帶來價值。如果在這個過程中該軟體能成為永續經營的項目,那就是開發者和使用者雙贏的局面。

位元詩人喜歡用開源技術來解決各式各樣的問題,但必要時對專有技術也不排斥。閒暇之餘,位元詩人將所學寫成文章,放在這個網站上和大家分享。