位元詩人 [Perl] 程式設計教學:使用雜湊 (Hash) 或關連式陣列 (Associative Array)

Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email

前言

雜湊 (hash) 或關連式陣列 (associative array) 是以鍵/值對為儲存單位的非線性容器,在 Perl 中相當實用。

建立雜湊

以下程式建立一個空雜湊:

my %h = ();

以下程式建立一個有儲存鍵/值對的雜湊:

my %h = (
    one => "eins",
    two => "zwei",
    three => "drei",
);

也可以先建立空雜湊後再逐一填入鍵/值對:

my %h = ();

$h{one} = "eins";
$h{two} = "zwei";
$h{three} = "drei";

如果雜湊的鍵/值對都是字串,其實有更簡便的建立方式:

my %h = qw(
    one eins
    two zwei
    three drei
);

因為 qw 會建立一個字串串列,而字串串列會自動成對加入雜湊。從另一個觀點來看,=> 其實只是逗號的另一個寫法,故 qw 的寫法也通用。

存取雜湊的值

雜湊以鍵為索引,藉由鍵取值。見下例:

my %h = (
    one => "eins",
    two => "zwei",
    three => "drei",
);

$h{one} eq "eins" or die "Wrong value";
$h{two} eq "zwei" or die "Wrong value";
$h{three} eq "drei" or die "Wrong value";

在取值時,取回的是純量,故要寫成 $h{one},一開始易寫錯,需注意。

由於雜湊內部的演算法,取索引運算是單向的。使用雜湊時,只能以鍵取值,不能以值取鍵;此外,鍵不能重覆而值可以。

確認鍵值對是否存在

使用 exists() 函式可確認鍵值對存在與否,如下例:

my %h = (
    one => "eins",
    two => "zwei",
    three => "drei",
);

exists($h{one}) or die "It should exist";
!exists($h{four}) or die "It should not exist";

exists()defined() 兩者的意義是不同的。前者會確認鍵/值對是否存在,而後者會確認值本身是否有定義。

移除鍵值對

使用 delete() 函式可刪除鍵值對。參考以下程式碼:

my %h = (
    one => "eins",
    two => "zwei",
    three => "drei",
);

exists($h{one}) or die "It should exist";

delete $h{one};
!exists($h{one}) or die "It should not exist";

將鍵值對設為未定義不會刪除該鍵值對。參考以下程式碼:

my %h = (
    one => "eins",
    two => "zwei",
    three => "drei",
);

exists($h{one}) or die "It should exist";

$h{one} = undef;
exists($h{one}) or die "It should exist";

請注意兩者的差別。

走訪雜湊

可透過鍵來走訪雜湊,這時候會搭配 keys 函式。參考下例:

my %h = (
    one => "eins",
    two => "zwei",
    three => "drei",
);

for my $k (keys %h) {
    print "$k: $h{$k}\n";
}

keys 函式會以串列的形式回傳雜湊的鍵,故可以用 for 走訪。

如果不需要鍵,也可以直接走訪值,這時候會搭配 values 函式。參考下例:

my %h = (
    one => "eins",
    two => "zwei",
    three => "drei",
);

for my $v (values %h) {
    print "$v\n";
}

values 會以串列的形式回傳雜湊的值,故可以用 for 走訪。

如果同時需要鍵/值對,也可搭配 each 函式來走訪雜湊。參考下例:

my %h = (
    one => "eins",
    two => "zwei",
    three => "drei",
);

while (my ($k, $v) = each %h) {
    print "$k: $v\n";
}

注意這時會用 while 而非 for 來走訪。

有序雜湊 (Ordered Hash)

如果多執行幾次程式,會發現每次走訪雜湊的順序相異,因為雜湊在儲存鍵/值對時不會儲存其順序。Perl 沒有內建的有序雜湊,如果要保存雜湊的儲存順序,要將鍵的順序另存在一個陣列中,藉由設置鍵陣列的順序來控制取出值的順序。可見下例:

my %h = (
    one => "eins",
    two => "zwei",
    three => "drei",
);

my @order = ();
push @order, "one", "two", "three";

for my $k (@order) {
    print "$k: $h{$k}\n";
}

模擬多維陣列

藉由操作雜湊的鍵,可以用雜湊來模擬多維陣列。見下例:

my %mtx = ();

for (my $i = 0; $i < 3; $i++) {
    for (my $j = 0; $j < 3; $j++) {
        $mtx{$i, $j} = ($i+1) * ($j+1);   
    }

}

$mtx{1, 2} == 6 or die "Wrong value";

這個程式能成立的關鍵在於 $mtx{$i, $j} 中會將 $i, $j 自動合併成一個字串,看起來就很像多維陣列。由於 Perl 5 後支援參考 (reference),現在比較少用這個技巧。如果陣列比較稀疏或是其索引為字串,仍可用這個技巧來建立多維陣列。

取出多個雜湊值

有時候我們會看到 @hash{$a, $b, $c} 的寫法,讀者可能會感到疑惑。@hash{$a, $b, $c} 代表我們得到的值是一個陣列,該陣列的值由原本的 %hash 中取得。見下例:

my %h = (
    one => "eins",
    two => "zwei",
    three => "drei",
);

for my $e (@h{"one", "two", "three"}) {
    print $e, "\n";
}

在本例中,我們對 %h 取值,由於一次要取多個值,故寫成 @h,但仍由原雜湊取值,故寫成 @h{"one", "two", "three"} 由鍵取值。

關於作者

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

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