第4章 ハッシュ変数

4-1. ハッシュ変数とは

ハッシュとは、連想配列とも呼ばれ、「キー」と「値」を1組のペアとして関連付けされた配列です。 ただし、配列自体は順序付けされていないところに特徴があります。
ハッシュの変数のことを、ハッシュ変数といい、% (パーセント) + 英字1文字から始まり、それ以降は数字、英字およびアンダースコア ( _ ) を用いることができます。また大文字と小文字が区別されますので、たとえば %a と %A は別物として扱われます。
pagetop

4-2. ハッシュ変数の操作

ハッシュ変数は、キーと値をペアにして、数値や文字列を代入します。
%fruit = ("red" => "apple", "yellow" => "banana");
上記の場合、red(キー)とapple(値)というペアと、yellow(キー)とbanana(値)というペアを、それぞれ%fruitに代入します。
ハッシュ変数の中の各要素(各ペア)は、$ハッシュ変数名 { キー } という形式で、それに対応する「値」を参照することができます。
したがって、前述の%fruitは、以下のように記述することもできます。
$fruit{"red"} = "apple";
$fruit{"yellow"} = "banana";
ハッシュ変数の各要素には、「キー」を指定することで「値」にアクセスすることができます。
%fruit = ("red" => "apple", "yellow" => "banana", "purple" => "grape");
print "$fruit{'red'}\n";
>  apple
ちなみに、「キー」の文字列が全て「英数字・アンダースコア」の場合には、クォーテーションによる括りを省略しても構いません。 ちょっとしたことですが、結構便利です。
# キーのクォーテーションを省略した例-1
%fruit = (red => "apple", yellow => "banana", purple => "grape");

# キーのクォーテーションを省略した例-2
print "$fruit{red}\n";
pagetop

4-3. ハッシュ関数

ハッシュを操作するための関数として、次のような関数が用意されています。
関数 内容
keys すべてのキーを取り出す
values すべての値を取り出す
each 1組のキーと値を取り出す
delete 特定の要素を取り除く
1. keys
すべての「キー」を取り出します。
ただし、取り出された配列の順序は一定でないことに注意します。 これはハッシュが順序付けられた配列ではないためです。
%fruit = (red => "apple", yellow => "banana", purple => "grape");
@file = keys %fruit;

print "@file\n";
>  purple yellow red
2. values
すべての「値」を取り出します。
keys関数同様、取り出された配列の順序は一定ではありません。
%fruit = (red => "apple", yellow => "banana", purple => "grape");
@file = values %fruit;

print "@file\n";
>  grape banana apple
3. each
1組の「キー」と「値」を取り出します。
%fruit = (red => "apple", yellow => "banana", purple => "grape");
($key, $val) = each %fruit;

print "$key : $val\n";
>  purple : grape 
しかしながら、上記の場合、どのペアが取り出されるかが不定であり、しかも1組だけが抽出されるので、通常each関数の場合は次のようにwhile構文と併用した形で使用されます。
%fruit = (red => "apple", yellow => "banana", purple => "grape");

# ハッシュの全要素を展開
while ( my ($key, $val) = each %fruit ) {
	print "$key : $val\n";
}
>  purple : grape
>  yellow : banana
>  red : apple
4. delete
特定の要素を取り除きます。
変数によるキーを指定することで、そのキーと値のペアをハッシュから削除します。
%fruit = (red => "apple", yellow => "banana", purple => "grape");

# 要素を削除(yellow - banana の要素)
delete $fruit{yellow};

# 残りの要素を全て展開
while ( my ($key, $val) = each %fruit ) {
        print "$key : $val\n";
}
>  purple : grape
>  red : apple
ちなみに、上記のdelete関数は特定の要素を削除する場合ですが、全要素を削除する場合には、空リストを代入すると、全要素が削除されます。
%fruit = ();
ただし、上記の場合中身が空ですが、定義値のままです。 もし、未定義値にしたい場合には、undef関数を使用します。
undef %fruit;
pagetop

4-4. ハッシュスライス

配列変数に対する場合と同様に、ハッシュのスライスを使用すれば、各要素の集合にアクセスすることができます。
たとえば、次のようなメンバーの年齢データがあったとします。
%age = (tom => 21, mike => 19, kent => 25, nancy => 17);
これをスライスを使うと、次のように記述できます。先頭が、%ではなく@であることに注意してください。
@age{ qw(tom mike kent nancy) } = (21,19,25,17);
以下に、コード例で見てみましょう。
tomとmikeが今月誕生日を迎えました。年齢を更新してみましょう。
use strict;

# 年齢データ
my %age = (tom => 21, mike => 19, kent => 25, nancy => 17);

# tomとmikeを更新
@age{ qw(tom mike) } = (22,20);

# データの中身を見る
while ( my ($key, $val) = each %age ) {
	print "$key : $val\n";
}
>  nancy : 17
>  kent : 25
>  tom : 22
>  mike : 20
また、スライスを使うことで、任意の要素の値をリストとして取り出すこともできます。
use strict;

# 年齢データ
my %age = (tom => 21, mike => 19, kent => 25, nancy => 17);

# kentとnancyの年齢を取り出す
my @data = @age{ qw(kent nancy) };

# リストの中身を見る
print "@data\n";
>  25 17
pagetop

4-5. ハッシュを代入順に並べる

ハッシュの配列は、順番どおりには並ばないことは前に述べました。
しかしながら、どうしてもキーや値を順番通りに取り出したい場合には、実はTie::IxHashモジュールを利用することで実現できます。
使い方によっては、大変便利なモジュールですので、ご紹介しておきます。
Tie::IxHashの使い方のポイントは、必ず対象のハッシュを呼び出す前に、tie関数で関連付けしておきます。
use strict;
use Tie::IxHash;

# %hashを関連付け
tie ( my %hash, 'Tie::IxHash' );

# ハッシュに代入
%hash = (
		Sun => 0,
		Mon => 1,
		Tue => 2,
		Wed => 3,
	);

# 全要素を展開
while( my ($key, $val) = each %hash ) {
	print "$key : $val\n";
}
>  Sun : 0
>  Mon : 1
>  Tue : 2
>  Wed : 3
上記のように、全てのキーと値を代入順に取り出すことができます。
ちなみに、Tie::IxHashモジュールは標準モジュールではありませんので、各自でインストールする必要があります。
ただし、PurePerl(すべてPerl言語で書かれたもの)のため、Perlライブラリ(jcode.pl, cgi-lib.pl等)のように、ホームディレクトリの中に置いて使用することが可能です。
pagetop

4-6. 多次元ハッシュ

ハッシュは、配列と同様に多次元に展開させることができます。ハッシュの中にハッシュを作ることができます。
以下のハッシュは、いわゆる二次元ハッシュになります。
%hash = (
	A => { a => 1, b => 2, c => 3 },
	B => { d => 4, e => 5, f => 6 },
);
波括弧(ブレース)の部分を、1つのスカラーと読み替えると、分かりやすいかもしれません。
%hashにおいて、キー「A」に対する値の中の最初のキー「a」の値「1」にアクセスするには、添字を使用して次のように記述することができます。
%hash = (
	A => { a => 1, b => 2, c => 3 },
	B => { d => 4, e => 5, f => 6 },
);

print "$hash{A}{a}\n";
>  1
リファレンスを使って、キー「A」に対する値(配列)を展開するには、次のように記述します。
%hash = (
	A => { a => 1, b => 2, c => 3 },
	B => { d => 4, e => 5, f => 6 },
);

while ( my ($key, $val) = each %{$hash{A}} ) {
	print "$key : $val\n";
}
>  c : 3
>  a : 1
>  b : 2
%hashの中の全てのキーと値を展開するには、次のように記述します。
%hash = (
	A => { a => 1, b => 2, c => 3 },
	B => { d => 4, e => 5, f => 6 },
);

foreach my $tmp ( keys %hash ) {
	while ( my ($key, $val) = each %{$hash{$tmp}} ) {
		print "$key : $val\n";
	}
}
>  c : 3
>  a : 1
>  b : 2
>  e : 5
>  d : 4
>  f : 6
%hashに対して、二次元のキーと値をさらに追加してみましょう。
%hash = (
	A => { a => 1, b => 2, c => 3 },
	B => { d => 4, e => 5, f => 6 },
);

# キー「C」に対する二次元の値を追加
$hash{C} = { g => 7, h => 8, i => 9 };

while ( my ($key, $val) = each %{$hash{C}} ) {
	print "$key : $val\n";
}
>  h : 8
>  g : 7
>  i : 9
特定のキーに対する値に、要素を追加してみましょう。
%hash = (
	A => { a => 1, b => 2, c => 3 },
	B => { d => 4, e => 5, f => 6 },
);

# キー「A」に対する値に要素追加
$hash{A}{x} = 7;

while ( my ($key, $val) = each %{$hash{A}} ) {
	print "$key : $val\n";
}
>  c : 3
>  a : 1
>  b : 2
>  x : 7
pagetop