Perl Perl_5 リファレンス
Perl リファレンス 15 データ構造 ハッシュのハッシュ (d018)

目次 - Perl Index
Theme
Perl について、復習を兼ねて断片的な情報を掲載して行く連載その d018 回。
Perl で、perldsc - perldoc.jp で解説されているデータ構造から「 ハッシュのハッシュ 」の生成とアクセスの方法を確認する。
・ ハッシュのハッシュ
・ [ 生成 ] ファイルからの読み込みによる生成
・ [ 生成 ] サブルーチンの呼び出しによる生成
・ [ 生成 ] 既存のハッシュのハッシュに key/value を追加
・ [ アクセス ] ハッシュのハッシュ value にアクセス
・ [ アクセス ] ハッシュ全体を一度に出力
・ [ アクセス ] ハッシュ全体をソートして出力
・ [ アクセス ] ハッシュ全体をメンバーの数でソートして出力
・ [ アクセス ] key にランクを設定してソートする
ハッシュのハッシュ
「 ハッシュのハッシュ 」( Hash of Hash : HoH ) は次のような構造です。
%HoH = (
key00 => {
kv00 => "v00",
kv01 => "v01",
},
key10 => {
kv10 => "v10",
kv11 => "v11",
kv12 => "v12",
},
key20 => {
kv20 => "v20",
kv21 => "v21",
},
);
[ 生成 ] ファイルからの読み込みによる生成
外部ファイルを読み込んで、その各行をハッシュ key/value としてハッシュの key に格納する方法を確認します。
# reading from file
# flintstones: lead=fred pal=barney wife=wilma pet=dino
while( <> ){
next unless s/^(.*?):\s*//;
my $who = $1;
foreach my $field ( split ){
my ($key, $value) = split /=/, $field;
$HoH{$who}{$key} = $value;
}
}
一時変数をもう少し利用して同様の処理も可能です。
# reading from file; more temps
while( <> ){
next unless s/^(.*?):\s*//;
my $who = $1;
my $rec = {};
$HoH{$who} = $rec;
foreach my $field ( split ){
my ($key, $value) = split /=/, $field;
$rec->{$key} = $value;
}
}
これは、(d017) で確認したデータ構造「 ハッシュの配列 」( Array of Hash ) の生成とほとんど同じです。
動作確認のために次のテキストファイルを準備しました。
Zenam: Son=Bellri Mother=Wilmit
Surugan: Daughter=Aida Father=Gusion
Megafauna: Captains=Donyell Mechanic=Happa Helmsman=Steer Bridge-Chief=Gisela
これを上記コードで処理すると、次のデータ構造が得られます。
use Data::Dumper;
print Dumper \%HoH;
__END__
# ハッシュのハッシュ
$VAR1 = {
'Zenam' => {
'Son' => 'Bellri',
'Mother' => 'Wilmit'
},
'Meafauna' => {
'Helmsman' => 'Steer',
'Mechanic' => 'Happa',
'Bridge-Chief' => 'Gisela',
'Captains' => 'Donyell'
},
'Surugan' => {
'Father' => 'Gusion',
'Daughter' => 'Aida'
}
};
[ 生成 ] サブルーチンの呼び出しによる生成
サブルーチンの戻り値を直接無名ハッシュとしてハッシュの key/value として格納する方法を確認します。
# calling a function that returns a key,value hash
foreach my $group ( "simpsons", "jetsons", "flintstones" ){
$HoH{$group} = { get_family($group) };
}
同様の処理は、次のように一時変数を利用して行うこともできます。
# likewise, but using temps
foreach my $group ( "simpsons", "jetsons", "flintstones" ){
my %members = get_family($group);
$HoH{$group} = { %members };
}
これは、(d016) で「 配列のハッシュ 」( Hash of Array ) を生成した処理とほとんど同じで、 最終的にハッシュに格納する際に無名ハッシュコンストラクタ「 { } 」を利用している部分が異なります。
sub get_family {
my $group = shift;
if( $group eq 'Zenam' ){
qw/Son Bellri Mother Wilmit/;
}elsif( $group eq 'Surugan' ){
qw/Daughter Aida Father Gusion/;
}
}
my %HoH;
foreach my $group ( "Zenam", "Surugan" ){
$HoH{$group} = { get_family($group) };
}
サブルーチンが返す値は、単なる文字列リストですが、無名ハッシュコンストラクタ「 { } 」によって順番に key/value として設定され、「 ハッシュのハッシュ 」が生成されます。
無名配列コンストラクタ「 [ ] 」を代わりに使うと「 配列のハッシュ 」が生成できます。
ハッシュ「 %HoH 」のデータ構造は次のようになります。
use Data::Dumper;
print Dumper \%HoH;
__END__
$VAR1 = {
'Zenam' => {
'Son' => 'Bellri',
'Mother' => 'Wilmit'
},
'Surugan' => {
'Father' => 'Gusion',
'Daughter' => 'Aida'
}
};
[ 生成 ] 既存のハッシュのハッシュに key/value を追加
既にハッシュのハッシュが設定されたハッシュに新たな key/value を追加する方法を確認します。
ハッシュのハッシュにハッシュを追加したいので、追加のための一時変数にもハッシュを利用すると便利なようです。
# append new members to an existing family
%new_folks = (
wife => "wilma",
pet => "dino",
);
foreach my $what ( keys %new_folks ){
$HoH{flintstones}{$what} = $new_folks{$what};
}
例えば次のようなコードを実行すると、
my %new_folks = (
Friend => 'Nored',
Instructor => 'Dellensen',
);
foreach my $what (keys %new_folks){
$HoH{Zenam}{$what} = $new_folks{$what};
}
データ構造は次のようになります。
use Data::Dumper;
print Dumper \%HoH;
__END__
$VAR1 = {
'Zenam' => {
'Friend' => 'Nored',
'Son' => 'Bellri',
'Mother' => 'Wilmit',
'Instructor' => 'Dellensen'
},
'Surugan' => {
'Father' => 'Gusion',
'Daughter' => 'Aida'
}
};
[ アクセス ] ハッシュのハッシュ value にアクセス
ハッシュ「 %HoH 」の value にセットした無名ハッシュの value にアクセスするには、次の記法が利用可能です。
# one element
${$HoH{Zenam}}{Son}
$HoH{Zenam}->{Son}
$HoH{Zenam}{Son}
別の value にアクセスしたい場合は、ハッシュの key を操作します。
# another element
$HoH{simpsons}{lead} =~ s/(\w)/\u$1/;
[ アクセス ] ハッシュ全体を一度に出力
ハッシュ「 %HoH 」に格納した各ハッシュの全体を出力するには、次のように記述します。
# print the whole thing
foreach my $family ( keys %HoH ){
print "$family: { ";
foreach my $role ( keys %{ $HoH{$family} } ){
print "$role=$HoH{$family}{$role} ";
}
print "}\n";
}
このコードでは、例えば次のような出力が得られます。
Zenam: { Son=Bellri Mother=Wilmit }
Surugan: { Father=Gusion Daughter=Aida }
[ アクセス ] ハッシュ全体をソートして出力
ハッシュ全体をそれなりにソートして出力するには、次のように記述します。
# print the whole thing somewhat sorted
foreach my $family ( sort keys %HoH ){
print "$family: { ";
foreach my $role ( sort keys %{ $HoH{$family} } ){
print "$role=$HoH{$family}{$role} ";
}
print "}\n";
}
親のハッシュ「 %HoH 」と子のハッシュ「 %{ $HoH{$family} } 」の key を取り出す際に、「 keys 」と合わせて「 sort 」を利用することで、それなりに value のソートを行っています。
このコードでは、例えば次のような出力が得られます。
Surugan: { Daughter=Aida Father=Gusion }
Zenam: { Mother=Wilmit Son=Bellri }
前項の出力と比較すると、確かにそれなりのソートが行われていることがわかります。
[ アクセス ] ハッシュ全体をメンバーの数でソートして出力
ハッシュ全体をメンバーの数で比較して多い順 ( 降順 ) でソートして出力するには、次のように記述します。
# print the whole thing sorted by number of members
foreach my $family (
sort{ keys %{$HoH{$b}} <=> keys %{$HoH{$a}} } keys %HoH)
{
print "$family: { ";
foreach my $role ( sort keys %{$HoH{$family}} ){
print "$role=$HoH{$family}{$role} ";
}
print "}\n";
}
このコードでは、例えば次のような出力が得られます。
Zenam: { Friend=Nored Instructor=Dellensen Mother=Wilmit Son=Bellri }
Surugan: { Daughter=Aida Father=Gusion }
メンバー数 ( key/value ) の多いハッシュから順に出力されていることがわかります。スペースシップ演算子「 <=> 」を利用したソートは (d016) の「 配列のハッシュ 」でも利用しました。
[ アクセス ] key にランクを設定してソートする
予めソートする際の順番 ( ランク ) を設定しておくことで、key を任意の順番で並び替えることができます。
# establish a sort order (rank) for each role
my $i = 0;
foreach (qw(lead wife son daughter pal pet)){ $rank{$_} = ++$i }
# now print the whole thing sorted by number of members
foreach my $family (
sort{ keys %{$HoH{$b}} <=> keys %{$HoH{$a}} } keys %HoH)
{
print "$family: { ";
# and print these according to rank orser
foreach my $role (
sort{ $rank{$a} <=> $rank{$b} } keys %{$HoH{$family}} )
{
print "$role=$HoH{$family}{$role} ";
}
print "}\n";
}
次のようにランクを指定してみます。
my %rank;
my $i = 0;
foreach (qw(Son Daughter Father Mother Friend Instructor)){
$rank{$_} = ++$i }
すると次の結果が得られます。
Zenam: { Son=Bellri Mother=Wilmit Friend=Nored Instructor=Dellensen }
Surugan: { Daughter=Aida Father=Gusion }
ランク付けした通りに key がソートされていることがわかります。
NEXT
次回は、「 複雑なレコード 」のデータ構造を確認します。
参考情報は書籍「 続・初めての Perl 改訂版 」, 「 Effective Perl 第 2 版 」を中心に perldoc, Wikipedia および各 Web サイト。それと詳しい先輩。
目次 - Perl Index
Perl mp2 翻訳 Web コンテンツ圧縮の FAQ (d228)
Perl mp2 翻訳 既知のブラウザのバグの回避策をいくつか (d227)
Perl mp2 翻訳 Perl と Apache でのキュートなトリック (d226)
Perl mp2 翻訳 テンプレートシステムの選択 (d225)
Perl mp2 翻訳 大規模 E コマースサイトの構築 (d224)
Perl mp2 翻訳 チュートリアル (d223)
Perl mp2 翻訳 既知のブラウザのバグの回避策をいくつか (d227)
Perl mp2 翻訳 Perl と Apache でのキュートなトリック (d226)
Perl mp2 翻訳 テンプレートシステムの選択 (d225)
Perl mp2 翻訳 大規模 E コマースサイトの構築 (d224)
Perl mp2 翻訳 チュートリアル (d223)