Perl 二維數組教程
非常簡短的(9php.com)一個二維數組教程,由鄙人翻譯完成。
最新版本可以從這裏獲取(POD 格式):
http://svn.perlchina.org/trunk/POD2-CN/lib/POD2/CN/perllol.pod
[code]NAME
perllol - 操作數組的(9php.com)數組(二維數組)
說明
聲明和訪問數組的(9php.com)數組
創建一個數組的(9php.com)數組(有時也可以叫“列表的(9php.com)列表”,不過不太準確)真是再簡
單也不過了。它相當容易理解,並且本文中出現的(9php.com)每個例子都有可能在實際應用
中出現。
數組的(9php.com)數組就是一個普通的(9php.com)數組(@AoA),不過可以接受兩個下標("$AoA[3][2])。
下面先定義一個這樣的(9php.com)數組: "
# 一個包含有“指向數組的(9php.com)引用”的(9php.com)數組
@AoA = (
[ "fred", "barney" ],
[ "george", "jane", "elroy" ],
[ "homer", "marge", "bart" ],
);
print $AoA[2][2];
bart
你可能已經注意到,外面的(9php.com)括號是圓括號,這是因爲我們想要給數組賦值,所以
需要圓括號。如果你*不*希望這裏是 @AoA,而是一個指向它的(9php.com)引用,那麼就得
這樣:
# 一個指向“包含有數組引用的(9php.com)數組”的(9php.com)引用
$ref_to_AoA = [
[ "fred", "barney", "pebbles", "bambam", "dino", ],
[ "homer", "bart", "marge", "maggie", ],
[ "george", "jane", "elroy", "judy", ],
];
print $ref_to_AoA->[2][2];
注意外面的(9php.com)括號現在變成了方括號,並且我們的(9php.com)訪問語法也有所改變。這時因爲
和 C 不同,在 Perl 中你不能自由地交換數組和引用(在 C 中,數組和指針在
很多地方可以互相代替使用)。$ref_to_AoA 是一個數組引用,而 @AoA 是一個
數組。同樣地,$AoA[2] 也不是一個數組,而是一個數組引用。所以下面這
兩行:
$AoA[2][2]
$ref_to_AoA->[2][2]
也可以用這兩行來代替:
$AoA[2]->[2]
$ref_to_AoA->[2]->[2]
這是因爲這裏有兩個相鄰的(9php.com)括號(不管是方括號還是花括號),所以你可以隨意
地省略箭頭符號。但是如果 $ref_to_AoA 後面的(9php.com)那個箭頭不能省略,因爲省略
了就沒法知道 $ref_to_AoA 到底是引用還是數組了 ^_^。
修改二維數組
前面的(9php.com)例子裏我們創建了包含有固定數據的(9php.com)二維數組,但是如何往其中添加新元
素呢?再或者如何從零開始創建一個二維數組呢?
首先,讓我們試着從一個文件中讀取二維數組。首先我們演示如何一次性添加一
行。首先我們假設有這樣一個文本文件:每一行代表了二維數組的(9php.com)行,而每一個
單詞代表了二維數組的(9php.com)一個元素。下面的(9php.com)代碼可以把它們儲存到 @AoA:
while (<>) {
@tmp = split;
push @AoA, [ @tmp ];
}
你也可以用一個函數來一次讀取一行:
for $i ( 1 .. 10 ) {
$AoA[$i] = [ somefunc($i) ];
}
或者也可以用一個臨時變量來中轉一下,這樣看起來更清楚些:
for $i ( 1 .. 10 ) {
@tmp = somefunc($i);
$AoA[$i] = [ @tmp ];
}
注意方括號 "[]" 在這裏非常重要。方括號實際上是數組引用的(9php.com)構造器。如果不
用方括號而直接寫,那就犯了很嚴重的(9php.com)錯誤:
$AoA[$i] = @tmp;
你看,把一個數組賦值給了一個標量,那麼其結果只是計算了 @tmp 數組的(9php.com)元素個
數,我想這肯定不是你希望的(9php.com)。
如果你打開了 "use strict",那麼你就得先定義一些變量然後才能避免警告:
use strict;
my(@AoA, @tmp);
while (<>) {
@tmp = split;
push @AoA, [ @tmp ];
}
當然,你也可以不要臨時變量:
while (<>) {
push @AoA, [ split ];
}
如果你知道想要放在什麼地方的(9php.com)話,你也可以不要 push(),而是直接進行賦值:
my (@AoA, $i, $line);
for $i ( 0 .. 10 ) {
$line = <>;
$AoA[$i] = [ split ' ', $line ];
}
甚至是這樣:
my (@AoA, $i);
for $i ( 0 .. 10 ) {
$AoA[$i] = [ split ' ', <> ];
}
你可能生怕 <> 在列表上下文會出差錯,所以想要明確地聲明要在標量上下文中
對 <> 求值,這樣可讀性會更好一些: (譯者注:列表上下文中,<>
返回所有的(9php.com)行,標量上下文中 <> 只返回一行。)
my (@AoA, $i);
for $i ( 0 .. 10 ) {
$AoA[$i] = [ split ' ', scalar(<>) ];
}
如果你想用 $ref_to_AoA 這樣的(9php.com)一個引用來代替數組,那你就得這麼寫:
while (<>) {
push @$ref_to_AoA, [ split ];
}
現在你已經知道如何添加新行了。那麼如何添加新列呢?如果你正在做數學中的(9php.com)
矩陣運算,那麼要完成類似的(9php.com)任務:
for $x (1 .. 10) {
for $y (1 .. 10) {
$AoA[$x][$y] = func($x, $y);
}
}
for $x ( 3, 7, 9 ) {
$AoA[$x][20] += func2($x);
}
想要訪問的(9php.com)某個元素是不是存在是無關緊要的(9php.com):因爲如果不存在那麼 Perl 會給
你自動創建!新創建的(9php.com)元素的(9php.com)值是 "undef"。
如果你想添加到一行的(9php.com)末尾,你可以這麼做:
# 添加新列到已存在的(9php.com)行
push @{ $AoA[0] }, "wilma", "betty";
注意我*沒有*這麼寫:
push $AoA[0], "wilma", "betty"; # 錯誤!
事實上,上面這句根本就沒法通過編譯!爲什麼?因爲 push() 的(9php.com)第一個參數必
須是一個真實的(9php.com)數組,不能是引用。
訪問和打印
現在是打印二維數組的(9php.com)時候了。那麼怎麼打印?很簡單,如果你只想打印一個元
素,那麼就這麼來一下:
print $AoA[0][0];
如果你想打印整個數組,那你可不能這樣:
print @AoA; # 錯誤!
因爲你這麼做只能得到一列引用,Perl 從來都不會自動地爲你解引用。作爲替
代,你必須得弄個循環或者是雙重循環。用 shell 風格的(9php.com) for() 語句就可以
打印整個二維數組:
for $aref ( @AoA ) {
print "\t [ @$aref ],\n";
}
如果你要用下標來遍歷的(9php.com)話,你得這麼做:
for $i ( 0 .. $#AoA ) {
print "\t elt $i is [ @{$AoA[$i]} ],\n";
}
或者這樣用雙重循環(注意內循環):
for $i ( 0 .. $#AoA ) {
for $j ( 0 .. $#{$AoA[$i]} ) {
print "elt $i $j is $AoA[$i][$j]\n";
}
}
如同你看到的(9php.com)一樣,它有點兒複雜。這就是爲什麼有時候用臨時變量能夠看起來
更簡單一些的(9php.com)原因:
for $i ( 0 .. $#AoA ) {
$aref = $AoA[$i];
for $j ( 0 .. $#{$aref} ) {
print "elt $i $j is $AoA[$i][$j]\n";
}
}
哦,好像還有點複雜,那麼試試這樣:
for $i ( 0 .. $#AoA ) {
$aref = $AoA[$i];
$n = @$aref - 1;
for $j ( 0 .. $n ) {
print "elt $i $j is $AoA[$i][$j]\n";
}
}
切片
切片是指數組的(9php.com)一部分。如果你想要得到多維數組的(9php.com)一個切片,那你得進行一些
下標運算。通過箭頭可以方便地爲單個元素解引用,但是訪問切片就沒有這麼好
的(9php.com)事了。當然,我們可以通過循環來取切片。
我們先演示如何用循環來獲取切片。我們假設 @AoA 變量的(9php.com)值和前面一樣。
@part = ();
$x = 4;
for ($y = 7; $y < 13; $y++) {
push @part, $AoA[$x][$y];
}
這個循環其實可以用一個切片操作來代替:
@part = @{ $AoA[4] } [ 7..12 ];
不過這個看上去似乎略微有些複雜。
下面再教你如何才能得到一個 *二維切片*, 比如 $x 從 4 到 8,$y 從 7 到
12,應該怎麼寫?
@newAoA = ();
for ($startx = $x = 4; $x <= 8; $x++) {
for ($starty = $y = 7; $y <= 12; $y++) {
$newAoA[$x - $startx][$y - $starty] = $AoA[$x][$y];
}
}
也可以省略掉中間的(9php.com)那層循環:
for ($x = 4; $x <= 8; $x++) {
push @newAoA, [ @{ $AoA[$x] } [ 7..12 ] ];
}
其實用 map 函數可以更加簡練:
@newAoA = map { [ @{ $AoA[$_] } [ 7..12 ] ] } 4 .. 8;
雖然你的(9php.com)經理也許會抱怨這種難以理解的(9php.com)代碼可能會帶來安全隱患,
然而這種觀點還是頗有爭議的(9php.com)(興許還可以更加安全也說不定 ^_^)。
換了是我,我會把它們放進一個函數中實現:
@newAoA = splice_2D( \@AoA, 4 => 8, 7 => 12 );
sub splice_2D {
my $lrr = shift; # 指向二維數組的(9php.com)引用
my ($x_lo, $x_hi,
$y_lo, $y_hi) = @_;
return map {
[ @{ $lrr->[$_] } [ $y_lo .. $y_hi ] ]
} $x_lo .. $x_hi;
}
參見
perldata(1), perlref(1), perldsc(1)
作者
Tom Christiansen <[email protected]>
Last update: Thu Jun 4 16:16:23 MDT 1998
翻譯者及翻譯聲明
本文由 flw ("[email protected]") 翻譯,翻譯成果首次出現在 *中國 Perl 協會*
http://www.perlchina.org) 的(9php.com)協作開發平臺上。
PerlChina.org 本着“在國內推廣 Perl” 的(9php.com)目的(9php.com),組織人員翻譯本文。讀者可
以在遵守原作者許可協議、尊重原作者及譯作者勞動成果的(9php.com)前提下,任意發佈或
修改本文。
本文作者用一種輕鬆地口吻簡要介紹了一下二維數組的(9php.com)用法,但是正因如此,文
中有些內容直譯過來反而很拗口、很難懂(畢竟中西方文化不同嘛),因此譯者
在翻譯時,對原文有較多的(9php.com)修改。喜歡閱讀英文原版勝過喜歡翻譯版的(9php.com)朋友們,
可以直接看原版 ^_^,同時也希望能夠理解譯者的(9php.com)一篇苦心,而不要在背後罵我
翻譯得不對就是了。
最新版本可以從這裏獲取(POD 格式):
http://svn.perlchina.org/trunk/POD2-CN/lib/POD2/CN/perllol.pod
[code]NAME
perllol - 操作數組的(9php.com)數組(二維數組)
說明
聲明和訪問數組的(9php.com)數組
創建一個數組的(9php.com)數組(有時也可以叫“列表的(9php.com)列表”,不過不太準確)真是再簡
單也不過了。它相當容易理解,並且本文中出現的(9php.com)每個例子都有可能在實際應用
中出現。
數組的(9php.com)數組就是一個普通的(9php.com)數組(@AoA),不過可以接受兩個下標("$AoA[3][2])。
下面先定義一個這樣的(9php.com)數組: "
# 一個包含有“指向數組的(9php.com)引用”的(9php.com)數組
@AoA = (
[ "fred", "barney" ],
[ "george", "jane", "elroy" ],
[ "homer", "marge", "bart" ],
);
print $AoA[2][2];
bart
你可能已經注意到,外面的(9php.com)括號是圓括號,這是因爲我們想要給數組賦值,所以
需要圓括號。如果你*不*希望這裏是 @AoA,而是一個指向它的(9php.com)引用,那麼就得
這樣:
# 一個指向“包含有數組引用的(9php.com)數組”的(9php.com)引用
$ref_to_AoA = [
[ "fred", "barney", "pebbles", "bambam", "dino", ],
[ "homer", "bart", "marge", "maggie", ],
[ "george", "jane", "elroy", "judy", ],
];
print $ref_to_AoA->[2][2];
注意外面的(9php.com)括號現在變成了方括號,並且我們的(9php.com)訪問語法也有所改變。這時因爲
和 C 不同,在 Perl 中你不能自由地交換數組和引用(在 C 中,數組和指針在
很多地方可以互相代替使用)。$ref_to_AoA 是一個數組引用,而 @AoA 是一個
數組。同樣地,$AoA[2] 也不是一個數組,而是一個數組引用。所以下面這
兩行:
$AoA[2][2]
$ref_to_AoA->[2][2]
也可以用這兩行來代替:
$AoA[2]->[2]
$ref_to_AoA->[2]->[2]
這是因爲這裏有兩個相鄰的(9php.com)括號(不管是方括號還是花括號),所以你可以隨意
地省略箭頭符號。但是如果 $ref_to_AoA 後面的(9php.com)那個箭頭不能省略,因爲省略
了就沒法知道 $ref_to_AoA 到底是引用還是數組了 ^_^。
修改二維數組
前面的(9php.com)例子裏我們創建了包含有固定數據的(9php.com)二維數組,但是如何往其中添加新元
素呢?再或者如何從零開始創建一個二維數組呢?
首先,讓我們試着從一個文件中讀取二維數組。首先我們演示如何一次性添加一
行。首先我們假設有這樣一個文本文件:每一行代表了二維數組的(9php.com)行,而每一個
單詞代表了二維數組的(9php.com)一個元素。下面的(9php.com)代碼可以把它們儲存到 @AoA:
while (<>) {
@tmp = split;
push @AoA, [ @tmp ];
}
你也可以用一個函數來一次讀取一行:
for $i ( 1 .. 10 ) {
$AoA[$i] = [ somefunc($i) ];
}
或者也可以用一個臨時變量來中轉一下,這樣看起來更清楚些:
for $i ( 1 .. 10 ) {
@tmp = somefunc($i);
$AoA[$i] = [ @tmp ];
}
注意方括號 "[]" 在這裏非常重要。方括號實際上是數組引用的(9php.com)構造器。如果不
用方括號而直接寫,那就犯了很嚴重的(9php.com)錯誤:
$AoA[$i] = @tmp;
你看,把一個數組賦值給了一個標量,那麼其結果只是計算了 @tmp 數組的(9php.com)元素個
數,我想這肯定不是你希望的(9php.com)。
如果你打開了 "use strict",那麼你就得先定義一些變量然後才能避免警告:
use strict;
my(@AoA, @tmp);
while (<>) {
@tmp = split;
push @AoA, [ @tmp ];
}
當然,你也可以不要臨時變量:
while (<>) {
push @AoA, [ split ];
}
如果你知道想要放在什麼地方的(9php.com)話,你也可以不要 push(),而是直接進行賦值:
my (@AoA, $i, $line);
for $i ( 0 .. 10 ) {
$line = <>;
$AoA[$i] = [ split ' ', $line ];
}
甚至是這樣:
my (@AoA, $i);
for $i ( 0 .. 10 ) {
$AoA[$i] = [ split ' ', <> ];
}
你可能生怕 <> 在列表上下文會出差錯,所以想要明確地聲明要在標量上下文中
對 <> 求值,這樣可讀性會更好一些: (譯者注:列表上下文中,<>
返回所有的(9php.com)行,標量上下文中 <> 只返回一行。)
my (@AoA, $i);
for $i ( 0 .. 10 ) {
$AoA[$i] = [ split ' ', scalar(<>) ];
}
如果你想用 $ref_to_AoA 這樣的(9php.com)一個引用來代替數組,那你就得這麼寫:
while (<>) {
push @$ref_to_AoA, [ split ];
}
現在你已經知道如何添加新行了。那麼如何添加新列呢?如果你正在做數學中的(9php.com)
矩陣運算,那麼要完成類似的(9php.com)任務:
for $x (1 .. 10) {
for $y (1 .. 10) {
$AoA[$x][$y] = func($x, $y);
}
}
for $x ( 3, 7, 9 ) {
$AoA[$x][20] += func2($x);
}
想要訪問的(9php.com)某個元素是不是存在是無關緊要的(9php.com):因爲如果不存在那麼 Perl 會給
你自動創建!新創建的(9php.com)元素的(9php.com)值是 "undef"。
如果你想添加到一行的(9php.com)末尾,你可以這麼做:
# 添加新列到已存在的(9php.com)行
push @{ $AoA[0] }, "wilma", "betty";
注意我*沒有*這麼寫:
push $AoA[0], "wilma", "betty"; # 錯誤!
事實上,上面這句根本就沒法通過編譯!爲什麼?因爲 push() 的(9php.com)第一個參數必
須是一個真實的(9php.com)數組,不能是引用。
訪問和打印
現在是打印二維數組的(9php.com)時候了。那麼怎麼打印?很簡單,如果你只想打印一個元
素,那麼就這麼來一下:
print $AoA[0][0];
如果你想打印整個數組,那你可不能這樣:
print @AoA; # 錯誤!
因爲你這麼做只能得到一列引用,Perl 從來都不會自動地爲你解引用。作爲替
代,你必須得弄個循環或者是雙重循環。用 shell 風格的(9php.com) for() 語句就可以
打印整個二維數組:
for $aref ( @AoA ) {
print "\t [ @$aref ],\n";
}
如果你要用下標來遍歷的(9php.com)話,你得這麼做:
for $i ( 0 .. $#AoA ) {
print "\t elt $i is [ @{$AoA[$i]} ],\n";
}
或者這樣用雙重循環(注意內循環):
for $i ( 0 .. $#AoA ) {
for $j ( 0 .. $#{$AoA[$i]} ) {
print "elt $i $j is $AoA[$i][$j]\n";
}
}
如同你看到的(9php.com)一樣,它有點兒複雜。這就是爲什麼有時候用臨時變量能夠看起來
更簡單一些的(9php.com)原因:
for $i ( 0 .. $#AoA ) {
$aref = $AoA[$i];
for $j ( 0 .. $#{$aref} ) {
print "elt $i $j is $AoA[$i][$j]\n";
}
}
哦,好像還有點複雜,那麼試試這樣:
for $i ( 0 .. $#AoA ) {
$aref = $AoA[$i];
$n = @$aref - 1;
for $j ( 0 .. $n ) {
print "elt $i $j is $AoA[$i][$j]\n";
}
}
切片
切片是指數組的(9php.com)一部分。如果你想要得到多維數組的(9php.com)一個切片,那你得進行一些
下標運算。通過箭頭可以方便地爲單個元素解引用,但是訪問切片就沒有這麼好
的(9php.com)事了。當然,我們可以通過循環來取切片。
我們先演示如何用循環來獲取切片。我們假設 @AoA 變量的(9php.com)值和前面一樣。
@part = ();
$x = 4;
for ($y = 7; $y < 13; $y++) {
push @part, $AoA[$x][$y];
}
這個循環其實可以用一個切片操作來代替:
@part = @{ $AoA[4] } [ 7..12 ];
不過這個看上去似乎略微有些複雜。
下面再教你如何才能得到一個 *二維切片*, 比如 $x 從 4 到 8,$y 從 7 到
12,應該怎麼寫?
@newAoA = ();
for ($startx = $x = 4; $x <= 8; $x++) {
for ($starty = $y = 7; $y <= 12; $y++) {
$newAoA[$x - $startx][$y - $starty] = $AoA[$x][$y];
}
}
也可以省略掉中間的(9php.com)那層循環:
for ($x = 4; $x <= 8; $x++) {
push @newAoA, [ @{ $AoA[$x] } [ 7..12 ] ];
}
其實用 map 函數可以更加簡練:
@newAoA = map { [ @{ $AoA[$_] } [ 7..12 ] ] } 4 .. 8;
雖然你的(9php.com)經理也許會抱怨這種難以理解的(9php.com)代碼可能會帶來安全隱患,
然而這種觀點還是頗有爭議的(9php.com)(興許還可以更加安全也說不定 ^_^)。
換了是我,我會把它們放進一個函數中實現:
@newAoA = splice_2D( \@AoA, 4 => 8, 7 => 12 );
sub splice_2D {
my $lrr = shift; # 指向二維數組的(9php.com)引用
my ($x_lo, $x_hi,
$y_lo, $y_hi) = @_;
return map {
[ @{ $lrr->[$_] } [ $y_lo .. $y_hi ] ]
} $x_lo .. $x_hi;
}
參見
perldata(1), perlref(1), perldsc(1)
作者
Tom Christiansen <[email protected]>
Last update: Thu Jun 4 16:16:23 MDT 1998
翻譯者及翻譯聲明
本文由 flw ("[email protected]") 翻譯,翻譯成果首次出現在 *中國 Perl 協會*
http://www.perlchina.org) 的(9php.com)協作開發平臺上。
PerlChina.org 本着“在國內推廣 Perl” 的(9php.com)目的(9php.com),組織人員翻譯本文。讀者可
以在遵守原作者許可協議、尊重原作者及譯作者勞動成果的(9php.com)前提下,任意發佈或
修改本文。
本文作者用一種輕鬆地口吻簡要介紹了一下二維數組的(9php.com)用法,但是正因如此,文
中有些內容直譯過來反而很拗口、很難懂(畢竟中西方文化不同嘛),因此譯者
在翻譯時,對原文有較多的(9php.com)修改。喜歡閱讀英文原版勝過喜歡翻譯版的(9php.com)朋友們,
可以直接看原版 ^_^,同時也希望能夠理解譯者的(9php.com)一篇苦心,而不要在背後罵我
翻譯得不對就是了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.