perlref - Perl 參照和巢狀資料結構
這是關於參照所有面向的完整文件。若要獲得較簡短的教學簡介,請參閱 perlreftut。
在 Perl 5 之前,表示複雜資料結構很困難,因為所有參照都必須是符號性的,即使如此,也很難參照變數,而不是符號表條目。Perl 現在不僅讓使用變數的符號參照變得更容易,還允許您對任何資料或程式碼片段擁有「硬式」參照。任何純量都可以包含硬式參照。由於陣列和雜湊包含純量,因此您現在可以輕鬆建立陣列的陣列、雜湊的陣列、陣列的雜湊、函式的雜湊陣列,等等。
硬式參照很聰明,它們會為您追蹤參照計數,當參照計數變為零時,會自動釋放被參照的項目。(自參照或循環資料結構中值的參照計數可能不會在沒有任何幫助的情況下變為零;請參閱「循環參照」以取得詳細說明。)如果該項目碰巧是一個物件,則會將該物件銷毀。請參閱perlobj以取得有關物件的更多資訊。(從某種意義上來說,Perl 中的一切都是物件,但我們通常將這個詞保留給已被正式「賜福」為類別套件的物件參照。)
符號參照是變數或其他物件的名稱,就像 Unix 檔案系統中的符號連結只包含檔案的名稱一樣。*glob
符號是一種符號參照。(符號參照有時稱為「軟式參照」,但請不要這樣稱呼它們;參照本身已經夠令人困惑了,不需要無用的同義詞。)
相反地,硬式參照更像是 Unix 檔案系統中的硬式連結:它們用於存取底層物件,而不用擔心它的(其他)名稱是什麼。當在沒有形容詞的情況下使用「參照」這個詞時,就像在以下段落中一樣,它通常是指硬式參照。
在 Perl 中很容易使用參照。只有一個最重要的原則:一般來說,Perl 沒有隱含參照或解除參照。當純量包含參照時,它總是表現得像一個簡單的純量。它不會神奇地開始成為陣列、雜湊或子常式;您必須明確地告訴它這樣做,方法是解除它的參照。
可以透過多種方式建立參考。
透過對變數、子常式或值使用反斜線運算子。(這與 C 中的 &(位址)運算子非常類似。)這通常會建立變數的另一個參考,因為符號表中已存在對該變數的參考。但符號表參考可能會消失,而你仍擁有反斜線傳回的參考。以下是一些範例
$scalarref = \$foo;
$arrayref = \@ARGV;
$hashref = \%ENV;
$coderef = \&handler;
$globref = \*foo;
無法使用反斜線運算子建立對 IO 句柄(檔案句柄或目錄句柄)的真實參考。你最多只能取得對類型 glob 的參考,這實際上是一個完整的符號表項目。但請參閱下方 *foo{THING}
語法的說明。不過,你仍然可以使用類型 glob 和 glob 參考,就像它們是 IO 句柄一樣。
可以使用方括號建立對匿名陣列的參考
$arrayref = [1, 2, ['a', 'b', 'c']];
這裡我們建立了一個對匿名陣列的參考,該陣列包含三個元素,其最後一個元素本身是對另一個包含三個元素的匿名陣列的參考。(稍後說明的多維語法可以用來存取這個陣列。例如,在上述範例之後,$arrayref->[2][1]
的值會是「b」。)
取得列舉清單的參考與使用方括號不同,而是與建立參考清單相同!
@list = (\$a, \@b, \%c);
@list = \($a, @b, %c); # same thing!
作為特殊情況,\(@foo)
會傳回對 @foo
內容的參考清單,而不是對 @foo
本身的參考。對 %foo
來說也是如此,但關鍵字參考是複製的(因為關鍵字只是字串,而不是成熟的純量)。
可以使用大括號建立匿名雜湊的參考
$hashref = {
'Adam' => 'Eve',
'Clyde' => 'Bonnie',
};
這些類似的匿名雜湊和陣列組成器可以自由混合,以產生您想要的複雜結構。以下所述的多維語法也適用於這些結構。上述值是文字,但變數和運算式也能正常運作,因為 Perl 中的指定運算子(即使在 local() 或 my() 內)是可執行陳述式,而非編譯時期的宣告。
由於大括號 (花括號) 也用於其他幾件事,包括區塊,因此您偶爾可能必須在陳述式的開頭取消大括號的歧義,方法是在前面加上 +
或 return
,以便 Perl 了解開啟的大括號並未開始區塊。使用花括號的經濟性和助記價值被認為值得偶爾多花點功夫。
例如,如果您想要一個函式來建立新的雜湊並傳回其參考,您有以下選項
sub hashem { { @_ } } # silently wrong
sub hashem { +{ @_ } } # ok
sub hashem { return { @_ } } # ok
另一方面,如果您想要其他意思,您可以這樣做
sub showem { { @_ } } # ambiguous (currently ok,
# but may change)
sub showem { {; @_ } } # ok
sub showem { { return @_ } } # ok
開頭的 +{
和 {;
始終用於消除運算式的歧義,使其表示雜湊參考或區塊。
可以使用沒有子常式名稱的 sub
來建立匿名子常式的參考
$coderef = sub { print "Boink!\n" };
請注意分號。除了內部程式碼不會立即執行之外,sub {}
不太像宣告,而更像運算子,例如 do{}
或 eval{}
。(但是,無論您執行該特定行幾次(除非您在 eval("...")
中),$coderef 仍會參考相同的匿名子常式。)
匿名子常式在 my() 變數方面扮演封閉的腳色,也就是在目前範圍內語法可見的變數。封閉是 Lisp 世界中的一個觀念,表示如果你在特定語法環境中定義一個匿名函式,即使在環境之外呼叫,它也會假裝在該環境中執行。
以人類的角度來說,這是一種在定義和呼叫子常式時傳遞參數的有趣方式。它有助於設定稍後執行的程式碼片段,例如回呼。你甚至可以使用它來執行物件導向的工作,儘管 Perl 已經提供不同的機制來執行此工作,請參閱 perlobj。
你也可以將封閉視為在不使用 eval() 的情況下撰寫子常式範本的方法。以下是封閉運作方式的一個小範例
sub newprint {
my $x = shift;
return sub { my $y = shift; print "$x, $y!\n"; };
}
$h = newprint("Howdy");
$g = newprint("Greetings");
# Time passes...
&$h("world");
&$g("earthlings");
它會列印
Howdy, world!
Greetings, earthlings!
特別注意,儘管匿名子常式執行時「my $x」已經超出範圍,但 $x 仍會繼續參照傳遞至 newprint() 的值。這就是封閉的全部意義。
順帶一提,這只適用於語法變數。動態變數會繼續像過去一樣運作。封閉並不是大多數 Perl 程式設計師一開始就需要煩惱的事情。
參照通常會由稱為建構函式的特殊子常式傳回。Perl 物件只是參照一種特殊類型的物件,而這種物件碰巧知道它與哪個套件相關聯。建構函式只是知道如何建立該關聯的特殊子常式。它們會從一個普通參照開始執行此操作,而它在成為物件的同時仍然是一個普通參照。建構函式通常會命名為 new()
。你可以間接呼叫它們
$objref = new Doggie( Tail => 'short', Ears => 'long' );
但在某些情況下可能會產生模稜兩可的語法,因此通常最好使用直接方法呼叫方法
$objref = Doggie->new(Tail => 'short', Ears => 'long');
use Term::Cap;
$terminal = Term::Cap->Tgetent( { OSPEED => 9600 });
use Tk;
$main = MainWindow->new();
$menubar = $main->Frame(-relief => "raised",
-borderwidth => 2)
只有在 use feature "indirect"
生效時,此間接物件語法才可用,而當要求 use v5.36
(或更高版本)時並非如此,最好完全避免間接物件語法。
如果你在假設它們存在的環境中解除參照,適當類型的參照可能會突然出現。由於我們尚未討論解除參照,因此我們無法向你展示任何範例。
可以透過使用特殊語法來建立參照,親切地稱為 *foo{THING} 語法。*foo{THING} 會傳回參照到 *foo 中的 THING 槽 (這是符號表條目,其中包含所有已知為 foo 的內容)。
$scalarref = *foo{SCALAR};
$arrayref = *ARGV{ARRAY};
$hashref = *ENV{HASH};
$coderef = *handler{CODE};
$ioref = *STDIN{IO};
$globref = *foo{GLOB};
$formatref = *foo{FORMAT};
$globname = *foo{NAME}; # "foo"
$pkgname = *foo{PACKAGE}; # "main"
其中大部分不言自明,但 *foo{IO}
值得特別注意。它會傳回 IO 句柄,用於檔案句柄 (perlfunc 中的「open」)、socket (perlfunc 中的「socket」 和 perlfunc 中的「socketpair」),以及目錄句柄 (perlfunc 中的「opendir」)。為了與 Perl 的舊版本相容,*foo{FILEHANDLE}
是 *foo{IO}
的同義詞,儘管它不建議使用,以鼓勵一致使用一個名稱:IO。在 v5.8 到 v5.22 之間的 Perl 中,它會發出不建議使用的警告,但此不建議使用已取消。
*foo{THING}
會傳回 undef,如果那個特定的 THING 尚未使用,但純量除外。*foo{SCALAR}
會傳回匿名純量的參照,如果 $foo 尚未使用。這可能會在未來的版本中變更。
*foo{NAME}
和 *foo{PACKAGE}
是例外,因為它們會傳回字串,而不是參照。這些會傳回類型全域本身的套件和名稱,而不是已指定給它的套件和名稱。所以,在 *foo=*Foo::bar
之後,*foo
在用作字串時會變成 "*Foo::bar",但 *foo{PACKAGE}
和 *foo{NAME}
會繼續產生 "main" 和 "foo"。
*foo{IO}
是 perldata 中「類型全域和檔案句柄」 中給定的 *HANDLE
機制的替代方案,用於將檔案句柄傳遞到子常式中或傳遞出子常式,或儲存到較大的資料結構中。它的缺點是它不會為您建立新的檔案句柄。它的優點是您不太可能透過類型全域指定來破壞比您想要的更多內容。(不過,它仍然會合併檔案和目錄句柄。)但是,如果您將輸入值指定給純量,而不是類型全域,就像我們在以下範例中所做的那樣,就不會發生這種情況。
splutter(*STDOUT); # pass the whole glob
splutter(*STDOUT{IO}); # pass both file and dir handles
sub splutter {
my $fh = shift;
print $fh "her um well a hmmm\n";
}
$rec = get_rec(*STDIN); # pass the whole glob
$rec = get_rec(*STDIN{IO}); # pass both file and dir handles
sub get_rec {
my $fh = shift;
return scalar <$fh>;
}
這就是建立參照的方法。到現在為止,您可能迫不及待地想知道如何使用參照來取回您遺失已久的資料。有幾種基本方法。
在變數或子程式名稱中,任何可以放置識別碼(或識別碼鏈)的地方,都可以用包含正確類型參考的簡單標量變數取代識別碼
$bar = $$scalarref;
push(@$arrayref, $filename);
$$arrayref[0] = "January";
$$hashref{"KEY"} = "VALUE";
&$coderef(1,2,3);
print $globref "output\n";
重要的是要了解,我們特別不會在那裡解除對 $arrayref[0]
或 $hashref{"KEY"}
的參考。標量變數的解除參考發生在它執行任何鍵值搜尋之前。比簡單標量變數更複雜的任何東西都必須使用以下方法 2 或 3。但是,「簡單標量」包括一個識別碼,它本身遞迴使用方法 1。因此,以下列印「howdy」。
$refrefref = \\\"howdy";
print $$$$refrefref;
在變數或子程式名稱中,任何可以放置識別碼(或識別碼鏈)的地方,都可以用傳回正確類型參考的區塊取代識別碼。換句話說,先前的範例可以這樣寫
$bar = ${$scalarref};
push(@{$arrayref}, $filename);
${$arrayref}[0] = "January";
${$hashref}{"KEY"} = "VALUE";
&{$coderef}(1,2,3);
$globref->print("output\n"); # iff IO::Handle is loaded
必須承認,在這種情況下使用大括號有點愚蠢,但區塊可以包含任何任意表達式,特別是帶下標的表達式
&{ $dispatch{$index} }(1,2,3); # call correct routine
由於可以省略 $$x
這種簡單情況的大括號,人們常常錯誤地將解除參考符號視為適當的運算子,並想知道它們的優先順序。如果它們是運算子,你就可以使用括號而不是大括號。事實並非如此。考慮以下差異;情況 0 是情況 1 的簡寫版本,不是情況 2
$$hashref{"KEY"} = "VALUE"; # CASE 0
${$hashref}{"KEY"} = "VALUE"; # CASE 1
${$hashref{"KEY"}} = "VALUE"; # CASE 2
${$hashref->{"KEY"}} = "VALUE"; # CASE 3
情況 2 也具有欺騙性,因為你存取的是稱為 %hashref 的變數,而不是透過 $hashref 解除參考到它可能參考的雜湊。那會是情況 3。
子程式呼叫和個別陣列元素的查詢經常出現,以至於使用方法 2 變得繁瑣。作為語法糖的一種形式,方法 2 的範例可以寫成
$arrayref->[0] = "January"; # Array element
$hashref->{"KEY"} = "VALUE"; # Hash element
$coderef->(1,2,3); # Subroutine call
箭號的左側可以是傳回參考的任何表達式,包括先前的解除參考。請注意,這裡的 $array[$x]
不等於 $array->[$x]
$array[$x]->{"foo"}->[0] = "January";
這是我們先前提到的案例之一,其中在 lvalue 語境中,參照可能會突然出現。在此陳述之前,$array[$x]
可能未定義。如果是這樣,它會自動定義為雜湊參照,以便我們可以在其中查詢 {"foo"}
。同樣地,$array[$x]->{"foo"}
會自動定義為陣列參照,以便我們可以在其中查詢 [0]
。這個程序稱為自動化。
這裡還有另一件事。在方括號下標之間的箭頭是可選的,因此您可以將上述內容縮小為
$array[$x]{"foo"}[0] = "January";
在只使用一般陣列的簡化情況下,會提供與 C 相同的多維陣列
$score[$x][$y][$z] += 42;
好吧,其實並不完全像 C 的陣列。C 不知道如何按需增加陣列。Perl 可以。
如果參照碰巧是對物件的參照,那麼可能會有方法來存取所參照的內容,而且您應該堅持使用這些方法,除非您在定義物件方法的類別套件中。換句話說,要友善,不要在沒有充分理由的情況下違反物件的封裝。Perl 沒有強制執行封裝。我們這裡不是極權主義者。不過,我們確實期望一些基本的禮貌。
如上所述,使用字串或數字作為參照會產生符號參照。使用參照作為數字會產生表示其在記憶體中儲存位置的整數。唯一有用的事情就是比較兩個參照的數字,看看它們是否參照同一個位置。
if ($ref1 == $ref2) { # cheap numeric compare of references
print "refs 1 and 2 refer to the same thing\n";
}
使用參照作為字串會產生其參照者的類型,包括 perlobj 中所述的任何套件祝福,以及以十六進位表示的數字位址。ref() 算子只會傳回參照指向的內容類型,而不包含位址。有關其用法和範例,請參閱 perlfunc 中的「ref」。
bless() 算子可用於將參照指向的物件與作為物件類別運作的套件關聯。請參閱 perlobj。
類型 glob 可以像參照一樣解除參照,因為解除參照語法總是表示所需的參照類型。因此,${*foo}
和 ${\$foo}
都表示相同的純量變數。
以下是如何將子程式呼叫內插到字串中的技巧
print "My sub returned @{[mysub(1,2,3)]} that time.\n";
其運作方式為,當在雙引號字串中看到 @{...}
時,它會被評估為一個區塊。區塊會建立一個匿名陣列的參考,其中包含呼叫 mysub(1,2,3)
的結果。因此,整個區塊會傳回一個陣列的參考,然後由 @{...}
解除參考並放入雙引號字串中。這種詭計也適用於任意表達式
print "That yields @{[$n + 5]} widgets\n";
類似地,傳回一個標量參考的表達式可以透過 ${...}
解除參考。因此,上述表達式可以寫成
print "That yields ${\($n + 5)} widgets\n";
在 Perl 中可以建立「循環參考」,這可能會導致記憶體外洩。當兩個參考包含彼此的參考時,就會發生循環參考,如下所示
my $foo = {};
my $bar = { foo => $foo };
$foo->{bar} = $bar;
您也可以使用單一變數建立循環參考
my $foo;
$foo = \$foo;
在這種情況下,變數的參考計數永遠不會達到 0,而且參考永遠不會被垃圾回收。這可能會導致記憶體外洩。
由於 Perl 中的物件實作為參考,因此也可以使用物件建立循環參考。想像一個 TreeNode 類別,其中每個節點都參考其父節點和子節點。任何具有父節點的節點都會成為循環參考的一部分。
您可以透過建立「弱參考」來中斷循環參考。弱參考不會遞增變數的參考計數,這表示物件可以超出範圍並被銷毀。您可以使用 Scalar::Util 模組匯出的 weaken
函數來弱化參考,或直接在 Perl 版本 5.35.7 或更新版本中使用 builtin::weaken
。
以下是如何讓第一個範例更安全
use Scalar::Util 'weaken';
my $foo = {};
my $bar = { foo => $foo };
$foo->{bar} = $bar;
weaken $foo->{bar};
從 $foo
到 $bar
的參考已被弱化。當 $bar
變數超出範圍時,它將被垃圾回收。下次您查看 $foo->{bar}
鍵的值時,它將會是 undef
。
這種遠距離動作可能會令人困惑,因此您應該小心使用弱化。您應該弱化將首先超出範圍的變數中的參考。這樣,較長存活的變數將包含預期的參考,直到它超出範圍。
我們說過,如果未定義,則參考會在必要時自動產生,但我們沒有說如果用作參考的值已經定義,但不是硬參考時會發生什麼情況。如果您將其用作參考,它將被視為符號參考。也就是說,標量的值將被視為變數的名稱,而不是指向(可能)匿名值的直接連結。
人們經常期望它像這樣運作。所以它確實如此。
$name = "foo";
$$name = 1; # Sets $foo
${$name} = 2; # Sets $foo
${$name x 2} = 3; # Sets $foofoo
$name->[0] = 4; # Sets $foo[0]
@$name = (); # Clears @foo
&$name(); # Calls &foo()
$pack = "THAT";
${"${pack}::$name"} = 5; # Sets $THAT::foo without eval
這很強大,但也有一點危險,因為有可能(非常誠懇地)打算使用硬參考,卻意外地使用了符號參考。為了防止這種情況,您可以說
use strict 'refs';
然後,在封閉區塊的其餘部分中,只允許硬參考。內部區塊可以用
no strict 'refs';
來反對它
local $value = 10;
$ref = "value";
{
my $value = 20;
print $$ref;
}
只有封裝變數(全域變數,即使是區域變數)對符號參考可見。詞彙變數(使用 my() 宣告)不在符號表中,因此對此機制不可見。例如
#非符號參考
$push = "pop on ";
print "${push}over";
符號參考周圍的括號可以僅用於將識別碼或變數名稱與表達式的其餘部分隔離,就像它們始終在字串中一樣。例如,
print ${push} . "over";
一直都表示要列印「pop on over」,即使 push 是保留字。這會被概括為在沒有封閉雙引號的情況下執行相同的工作,以便
print ${ push } . "over";
甚至
use strict 'refs';
${ bareword }; # Okay, means $bareword.
${ "bareword" }; # Error, symbolic reference.
在使用嚴格參考時,將具有相同的效果。此結構不被視為符號參考
$hash{ "aaa" }{ "bbb" }{ "ccc" }
類似地,由於所有使用單字進行的加下標,因此相同的規則適用於用於對雜湊進行加下標的任何裸字。因此,現在,您可以只寫
$hash{ aaa }{ bbb }{ ccc }
而不用擔心下標是否是保留字。在您確實希望執行類似
$hash{ shift }
您可以透過新增任何能使它成為多於單一字詞的內容,強制將解釋視為保留字
$hash{ shift() }
$hash{ +shift }
$hash{ shift @_ }
use warnings
pragma 或 -w 開關會在將保留字解釋為字串時警告您。但它不再會警告您使用小寫字詞,因為字串有效地加上引號。
已從 Perl 中移除偽雜湊。'fields' pragma 仍可使用。
如上所述,具有存取編譯該函數時可見的詞彙變數的匿名函數會建立封閉。即使它直到稍後才執行,例如在訊號處理常式或 Tk 回呼中,它仍會保留存取那些變數的權限。
將封閉用作函數範本允許我們產生許多作用相似的函數。假設您想要以顏色命名的函數,為各種顏色產生 HTML 字型變更
print "Be ", red("careful"), "with that ", green("light");
red() 和 green() 函數會很相似。若要建立這些函數,我們會將封閉指定給我們嘗試建立的函數名稱的類型全域變數。
@colors = qw(red blue green yellow orange purple violet);
for my $name (@colors) {
no strict 'refs'; # allow symbol table manipulation
*$name = *{uc $name} = sub { "<FONT COLOR='$name'>@_</FONT>" };
}
現在所有那些不同的函數似乎獨立存在。您可以呼叫 red()、RED()、blue()、BLUE()、green() 等。此技術可節省編譯時間和記憶體使用量,而且也不太容易出錯,因為語法檢查發生在編譯時間。若要建立適當的封閉,匿名子常式中的任何變數都必須是詞彙變數至關重要。這就是迴圈反覆變數上 my
的原因。
這是唯一提供封閉原型有意義的地方之一。如果您想要對這些函數的引數施加純量內容(對於這個特定範例來說可能不是明智的做法),您可以改用這個方式撰寫
*$name = sub ($) { "<FONT COLOR='$name'>$_[0]</FONT>" };
然而,由於原型檢查發生在編譯時間,因此上述的賦值發生得太晚,無法發揮太多作用。你可以將整個賦值迴圈放入 BEGIN 區塊中來解決這個問題,強制它在編譯期間發生。
存取隨著時間而變化的詞彙變數(例如上述 for
迴圈中的那些變數,基本上是對周圍詞彙範圍中元素的別名)只適用於匿名子常式,不適用於命名子常式。一般來說,命名子常式無法正確巢狀,而且只能在主程式包範圍中宣告。
這是因為命名子常式是在編譯時間建立的,因此它們的詞彙變數會在父區塊第一次執行時指派給父詞彙變數。如果父範圍第二次被輸入,它的詞彙變數會再次建立,而巢狀子常式仍然會參照舊的詞彙變數。
匿名子常式會在你每次執行 sub
算子時擷取,因為它們是即時建立的。如果你習慣在其他程式語言中使用具有自己私有變數的巢狀子常式,你必須在 Perl 中稍微努力一下。這種類型直觀的編碼會產生神秘的警告,說明「不會保持共用」,原因如上所述。例如,以下程式碼無法運作
sub outer {
my $x = $_[0] + 35;
sub inner { return $x * 19 } # WRONG
return $x + inner();
}
以下是可以解決的方法
sub outer {
my $x = $_[0] + 35;
local *inner = sub { return $x * 19 };
return $x + inner();
}
現在 inner() 只可以在 outer() 內部呼叫,因為匿名子常式有暫時的賦值。但是當它呼叫時,它可以正常存取 outer() 範圍中的詞彙變數 $x,當時機為 outer 被呼叫的時候。
這會產生一個有趣的效應,也就是建立一個局部於另一個函式的函式,這通常不受 Perl 支援。
從 v5.20.0 開始,可以使用後綴語法來使用參照。它的行為如 "使用參照" 中所述,但使用後綴符號和星號,而不是前綴符號。
例如
$r = \@a;
@b = $r->@*; # equivalent to @$r or @{ $r }
$r = [ 1, [ 2, 3 ], 4 ];
$r->[1]->@*; # equivalent to @{ $r->[1] }
在 Perl 5.20 和 5.22 中,必須使用 use feature 'postderef'
來啟用此語法。從 Perl 5.24 開始,不需要任何功能聲明即可使用它。
後綴取消參照應可在所有區塊(環繞符號)取消參照運作的情況下運作,並且應完全等效。此語法允許從左到右撰寫和閱讀取消參照。定義下列等效關係
$sref->$*; # same as ${ $sref }
$aref->@*; # same as @{ $aref }
$aref->$#*; # same as $#{ $aref }
$href->%*; # same as %{ $href }
$cref->&*; # same as &{ $cref }
$gref->**; # same as *{ $gref }
特別注意,$cref->&*
不等於 $cref->()
,並且可以服務於不同的目的。
可透過後綴取消參照功能來擷取 Glob 元素
$gref->*{SCALAR}; # same as *{ $gref }{SCALAR}
後綴陣列和標量取消參照可以在內插字串(雙引號或 qq
運算子)中使用,但前提是啟用了 postderef_qq
功能。啟用 postderef_qq
功能時,也支援內插後綴陣列最高索引存取(->$#*
)。
陣列和雜湊的數值切片也可以使用後綴取消參照符號來取得,等效關係如下
$aref->@[ ... ]; # same as @$aref[ ... ]
$href->@{ ... }; # same as @$href{ ... }
在 5.20.0 中新增的後綴鍵/值對切片,並在 perldata 的鍵/值雜湊切片區段 中記錄,也按預期運作
$aref->%[ ... ]; # same as %$aref[ ... ]
$href->%{ ... }; # same as %$href{ ... }
與後綴陣列一樣,後綴值切片取消參照可以在內插字串(雙引號或 qq
運算子)中使用,但前提是啟用了 postderef_qq
功能。
從 v5.22.0 開始,可以指定參照運算子。它執行別名運算,因此左手邊參照的變數名稱成為右手邊參照的別名
\$a = \$b; # $a and $b now point to the same scalar
\&foo = \&bar; # foo() now means bar()
必須使用 use feature 'refaliasing'
來啟用此語法。它是實驗性的,除非 no warnings 'experimental::refaliasing'
生效,否則預設會發出警告。
這些形式可以指定,並導致右手邊在純量上下文中求值
\$scalar
\@array
\%hash
\&sub
\my $scalar
\my @array
\my %hash
\state $scalar # or @array, etc.
\our $scalar # etc.
\local $scalar # etc.
\local our $scalar # etc.
\$some_array[$index]
\$some_hash{$key}
\local $some_array[$index]
\local $some_hash{$key}
condition ? \$this : \$that[0] # etc.
切片運算和括號導致右手邊在清單上下文中求值
\@array[5..7]
(\@array[5..7])
\(@array[5..7])
\@hash{'foo','bar'}
(\@hash{'foo','bar'})
\(@hash{'foo','bar'})
(\$scalar)
\($scalar)
\(my $scalar)
\my($scalar)
(\@array)
(\%hash)
(\&sub)
\(&sub)
\($foo, @bar, %baz)
(\$foo, \@bar, \%baz)
右手邊的每個元素都必須是對正確類型資料的參照。緊鄰陣列的括號(也可能是 my
/state
/our
/local
)將使陣列的每個元素成為右手邊參照的對應純量的別名
\(@a) = \(@b); # @a and @b now have the same elements
\my(@a) = \(@b); # likewise
\(my @a) = \(@b); # likewise
push @a, 3; # but now @a has an extra element that @b lacks
\(@a) = (\$a, \$b, \$c); # @a now contains $a, $b, and $c
禁止將該形式與 local
結合並在雜湊周圍加上括號(因為不清楚它們應該做什麼)
\local(@array) = foo(); # WRONG
\(%hash) = bar(); # WRONG
可以將對參照和非參照的指定組合在清單和條件三元表達式中,只要右手邊的值是左手邊每個元素的正確類型即可,儘管這可能會使程式碼混淆
(my $tom, \my $dick, \my @harry) = (\1, \2, [1..3]);
# $tom is now \1
# $dick is now 2 (read-only)
# @harry is (1,2,3)
my $type = ref $thingy;
($type ? $type eq 'ARRAY' ? \@foo : \$bar : $baz) = $thingy;
foreach
迴圈也可以為其迴圈變數採用參照建構函式,儘管語法僅限於以下之一,反斜線後加上任意的 my
、state
或 our
\$s
\@a
\%h
\&c
不允許使用括號。此功能對於陣列陣列或雜湊陣列特別有用
foreach \my @a (@array_of_arrays) {
frobnicate($a[0], $a[-1]);
}
foreach \my %h (@array_of_hashes) {
$h{gelastic}++ if $h{type} eq 'funny';
}
注意:別名無法正確地與封閉函數搭配使用。如果您嘗試從內部子常式或 eval
別名化詞彙變數,則別名只會在該內部子常式中可見,而且不會影響宣告變數的外層子常式。這種奇怪的行為可能會變更。
從 v5.26.0 開始,參考運算子可以出現在 my
、state
、our
或 local
之後。此語法必須使用 use feature 'declared_refs'
啟用。此語法為實驗性質,而且會預設發出警告,除非 no warnings 'experimental::refaliasing'
生效中。
此功能讓這些
my \$x;
our \$y;
等同於
\my $x;
\our $x;
它主要用於指派到參考(請參閱上方的「指派到參考」)。它也允許在宣告變數清單中僅對某些項目使用反斜線
my ($foo, \@bar, \%baz); # equivalent to: my $foo, \my(@bar, %baz);
您不能(有意義地)將參考用作雜湊的鍵。它會轉換成字串
$x{ \$a } = $a;
如果您嘗試取消參考鍵,它不會執行硬取消參考,而且您無法達成您嘗試執行的動作。您可能想要執行更類似於
$r = \@a;
$x{ $r } = $r;
然後,至少您可以使用 values(),它會是真正的參考,而不是 keys(),它不會是真正的參考。
標準的 Tie::RefHash 模組提供了一個方便的解決方法。
除了明顯的文件外,原始碼也可以提供指導。一些參考用法的病態範例可以在 Perl 原始碼目錄中的 t/op/ref.t 回歸測試中找到。
另請參閱 perldsc 和 perllol,了解如何使用參考建立複雜的資料結構,以及 perlootut 和 perlobj,了解如何使用參考建立物件。