2部 Perl言語仕様

ハッシュ

1.ハッシュへ値を代入

ハッシュ変数は、配列のインデックスが文字列となったもので、このインデックスを『キー』と呼びます。ハッシュの場合は要素の値がキーによって管理されます。

ハッシュの宣言

ハッシュを宣言するときは変数名の前にパーセント( % )を付けます。

%hash;

ハッシュへ値を代入

配列では、[ ] でインデックスを囲みましたが、ハッシュは、 { }でキーを囲みます。例えば社員の名前とメールアドレスを管理する場合、次のような方法でハッシュを使った管理ができます。

$hash{'Akai'} = 'akai@domain.com';
$hash{'Ishikawa'} = 'ishi@domain.com';
$hash{'Ueda'} = 'ueda@domain.com';

複数の値を一度にハッシュへ代入するには、キー、値の順番で記述します。

%hash = ('Akai', 'akai@domain.com', 'Ishikawa', 'ishi@domain.com');

=> 演算子を使うと読みやすくなります。

%hash = (
  "Akai" => 'akai@domain.com',
  "ihikawa" => 'ishi@domain.com',
);

また、ハッシュ変数をハッシュ変数に直接代入することによって、ハッシュをコピーすることもできます。

%new = %old;    # %oldの内容を%newにコピーする

2.ハッシュの値を参照

ハッシュも配列と同様に、ハッシュ用の参照書式があり、さらに参照用の関数も用意されています。

キーを使ってハッシュの要素を参照

ハッシュの各要素を参照するには、キーを { } で囲んで指定します。

%hash = ('Akai', 'akai@domain.com', 'Ishikawa', 'ishi@domain.com');
 print $hash{Akai};
> akai@domain.com

ハッシュは変数展開の対象にならないので、ダブルクォートで囲んでもそのままの表記が出力されます。

print "%hash\n";
> %hash

keys 関数

keys関数は、ハッシュ変数のすべてのキーをリスト値として返します。ハッシュ変数のすべてのキーのリストを配列に代入する場合は以下のとおりです。

@key = keys( %hash );

キーの格納には乱数を使った手法が使われるため、ハッシュ全体を出力する場合、その順番は保証されません。ですから、@keyには %hash のキーが順序を無視して格納されます。

values 関数

values関数は、ハッシュ変数のすべての値をリストにして返します。

@value = values( %hash );

ハッシュのすべての要素を処理

ハッシュの要素すべてを参照するには、for ループや foreach ループを使います。

%hash = ('Akai', 'akai@domain.com', 'Ishikawa', 'ishi@domain.com');
foreach $key ( keys %hash ) {
	print "key:$key : value:$hash{$key}", "\n";
}
> key:Ishikawa, value:ishi@domain.com
> key:Akai, value:akai@domain.com

出力される順番は予測できません。

sort関数でキーをソート

sort 関数は、keys関数が任意にリスト化したキーの順番を、文字コード順で並び替えることができます。

%hash = ('Akai', 'akai@domain.com', 'Ishikawa', 'ishi@domain.com');
foreach $key ( sort keys %hash ) { print "key:$key, value:$hash{$key}", "\n"; } > key:Akai, value:akai@domain.com > key:Ishikawa, value:ishi@domain.com

出力された順番はどのような環境で何度やり直しても同じです。

each 関数

each関数は、ハッシュから次の要素を取り出して、キーと値のペアを返します。すべての要素を取り出すと、未定義を返します。

each関数は、次のようにwhile文と組み合わせることにより、ハッシュのすべての要素にアクセスすることができます。

%hash = ('Akai', 'akai@domain.com', 'Ishikawa', 'ishi@domain.com');
while ( ($key, $value) = each(%hash) ) {
	print "key:$key, value:$value\n";
}
> key:Akai, value:akai@domain.com
> key:Ishikawa, value:ishi@domain.com

ハッシュ変数 %hash の各要素のキーと値を順に $key、$valueに代入しながら、ループを繰り返します。each関数はすべての要素を処理し終えると未定義値を返すので、while文がそれを受け取ってただちに終了します。

each関数はキーと値を順次取り出していくので、メモリを無駄に消費しません。その点、すべてのキーをリストにしてから処理するkeys 関数よりも、巨大なハッシュを扱う場合などに有効です。ただし、sortが利かないので、順番を気にする必要があるときは使えません。

キーに対応する値があるかチェック

指定したキーの値があるかを調べるには次のようにします。

# 指定したキー$key の値がある場合
if ( defined($count{$key}) ) {
	...
}

値が未定義の場合でもキーが存在する場合があります。このばあいは definedでは調べることができません。キーが存在するかどうかは、exists関数を使います。この関数は、ハッシュの要素を引数として受け取り、その要素が存在すれば真を、存在しなければ偽を返します。

# キー$key が登録されている場合
if ( exists($count{$key}) ) {
	...
}

ハッシュをリストコンテキストで評価

ハッシュをリストコンテキストで評価すると、キーと値が交互に並んだリスト値が得られます。このときのリスト値の順番はランダムなものになっています。

%hash = (a => 1, b => 2);
@list = %hash;
print "@list";
> b 2 a 1

ハッシュをリストコンテキストで評価したときのリスト値は、キーと値が交互に並びます。それをまたハッシュに代入すると、元のハッシュと同じ要素を持つハッシュを作ることができます。

%hash = (a => 1, b => 2);
@list = %hash;
%hash2 = @list;   # %hash2は%hashと同じ内容になる

ハッシュの要素数を調べる

ハッシュに入っている要素の個数は、keys関数で知ることができます。keys関数をスカラコンテキストで評価すると、引数に渡されたハッシュの要素の個数を返します。

$n = keys( %hash );

ハッシュの消去

ハッシュの要素を初期化するには、 次のよう空のリストを代入します。

%hash = ();

要素を削除するには、 次のように delete 関数を使います。

delete $hash{'Akai'};

3.テクニック

ハッシュの要素値で検索する

ハッシュの値をキーとした新しいハッシュを作成します。

%hash = ("曹操"=>"魏", "劉備"=>"蜀","孫権"=>"呉");
%hash_value = reverse %hash;
print $hash_value{'魏'};
> 曹操

上記よりもメモリを効率よく使うには次のようにします。

while ( ($key, $value) = each %hash ) {
	$hash_value{$value} = $key;
}

ただし、これらの方法はハッシュに同じ値がある場合には、最初に見つかったキーだけを見つけだします。

ハッシュを値でソートする

まず、キーか値のリストをソートする必要があります。

%hash = ("YAHOO"=>"YAHOO.CO.JP", "RHYTHM"=>"RFS.JP", "GOO"=>"GOO.NE.JP");
# キーによるソート
@keys = sort keys %hash;
print "@keys\n";
# 値によるソート
@keys = sort {
	$hash{$a} cmp $hash{$b}
} keys %hash;
print "@keys\n"

以下の例は、値を数値の降順でソートし、2つのキーが同値であればそれをキーの長さでソートし、それが失敗したならキーのASCII比較を行うものです。

@keys = sort {
	$hash{$b} <=> $hash{$a} ||
	length($b) <=> length($a) ||
	$a cmp $b
} keys %hash;

2つのハッシュからユニークなキーを取りだす

まず最初にハッシュからキーを取りだして、それを配列に格納します。

%seen = ();
for $element ( keys(%hash1), keys(%hash2) ) {
    $seen{$element}++;
}
@uniq = keys %seen;

簡潔な方法は以下のとおりです。

@uniq = keys %{{%hash1, %hash2}};

よりメモリを節約する場合は以下のとおりです。

%seen = ();
while ( defined ($key = each %hash1) ) {
	$seen{$key}++;
}
while ( defined ($key = each %hash2) ) {
	$seen{$key}++;
}
@uniq = keys %seen;

関連記事