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)一篇苦心,而不要在背後罵我
    翻譯得不對就是了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章