Perl Perl_5 リファレンス
Perl リファレンス 13 データ構造 配列のハッシュ (d016)

目次 - Perl Index
Theme
Perl について、復習を兼ねて断片的な情報を掲載して行く連載その d016 回。
Perl で、perldsc - perldoc.jp で解説されているデータ構造から「 配列のハッシュ 」の生成とアクセスの方法を確認する。
・ 配列のハッシュ
・ [ 生成 ] ファイルからの読み込みによる生成
・ [ 生成 ] サブルーチンの呼び出しによる生成
・ [ 生成 ] 既存の配列のハッシュに要素を追加
・ [ アクセス ] 配列のハッシュの value にアクセス
・ [ アクセス ] 配列全体を一度に出力
・ [ アクセス ] 配列の要素でソート
・ [ アクセス ] 配列の要素で多重ソート
配列のハッシュ
「 配列のハッシュ 」( Hash of Array : HoA ) は次のような構造です。
%HoA = (
key01 => [ "val00", "val01" ],
key02 => [ "val10", "val11" ],
key03 => [ "val20", "val21" ],
);
[ 生成 ] ファイルからの読み込みによる生成
外部ファイルを読み込んで、その各行を配列リファレンスとしてハッシュに格納する方法を確認します。
# reading from file
# flintstones: fred barney wilma dino
while( <> ){
next unless s/^(.*?):\s*//;
$HoA{$1} = [ split ];
}
このコードでは、前回 (d015) と同様にダイヤモンド演算子「 <> 」や演算子「 split 」、それから無名配列コンストラクタ「 [ ] 」を利用して、起動引数に指定したファイルの内容を読み込んでいます。
なお、前回の配列の配列 ( AoA ) の生成では、読み込んだファイルの各行をそのまま配列の要素に格納すれば良いだけでしたが、今回の配列のハッシュ ( HoA ) の生成では、各行をハッシュの key と value により分ける必要があります。
key と value のより分けは、置換演算子「 s/// 」(0x71) とキャプチャの参照 (0x5d) を利用して行います。
対象となるテキストファイルは、次のようなものです。
flintstones: fred barney wilma dino
rubbles: rubble betty bamm-bamm
others: slate arnold joe pearl
ここでは、行頭に「 family name 」があり、「 : 」をデリミタとして各人の「 personal name 」が続きます。
まずは、この行を次の正規表現で処理します。処理の対象はお馴染みの「 $_ 」です。
next unless s/^(.*?):\s*//;
これは、先頭から「 : 」までの文字列、つまり「 family name 」だけを「 (.*?) 」でキャプチャしつつ、「 : 」から空白文字「 \s* 」を削除します。
これにより、「 $_ 」にはマッチングパターンから外れた「 personal name 」の部分のみが残ります。
また、パターンにマッチングする部分がない行 ( ここでは空行 ) は演算子「 next 」(0x11) によってスキップされます。
次に、キャプチャした「 family name 」を「 $1 」で参照してハッシュの key とします。
ハッシュの value は、「 $_ 」に残った「 personal name 」の文字列を演算子「 split 」で分割して、無名配列コンストラクタ「 [ ] 」で配列化したものになります。
$HoA{$1} = [ split ];
こうして生成したハッシュの配列「 %HoA 」のデータ構造を確認してみます。
use Data::Dumper;
print Dumper \%HoA;
__END__
# Hash of Array
$VAR1 = {
'rubbles' => [
'rubble',
'betty',
'bamm-bamm'
],
'flintstones' => [
'fred',
'barney',
'wilma',
'dino'
],
'others' => [
'slate',
'arnold',
'joe',
'pearl'
]
};
ハッシュ全体「 %AoH 」の key として「 family name 」が、それらの value に「 personal name 」が配列として格納されていることがわかります。
同様の処理は、次のように一時変数を利用して行うこともできます。
# reading from file; more temps
while( my $line = <> ) {
# 空行をスキップ
next if $line =~ /\A\n\z/;
my ($who, $rest) = split /:\s*/, $line, 2;
my @fields = split ' ', $rest;
$HoA{$who} = [ @fields ];
}
ここでは、空行のスキップや演算子「 split 」の利用方法がやや異なっていることに注意します。
[ 生成 ] サブルーチンの呼び出しによる生成
サブルーチンの戻り値を無名配列としてハッシュの value に格納する方法を確認します。
# Calling a function that returns a list
foreach my $group ("simpsons", "jetsons", "flintstones" ) {
$HoA{$group} = [ get_family($group) ];
}
このコードでは、サブルーチン「 get_family() 」の戻り値を、無名配列コンストラクタ「 [ ] 」を通して配列リファレンスに変換しハッシュ「 %HoA 」の value として格納しています。
この時の key はサブルーチンの引数と同じ「 $group 」です。
具体的な処理を確認するために、サブルーチン「 get_family() 」を次のように定義しました。これは、引数の文字列をチェックして 3 つの family の「 personal name 」リストを返す単純なものです。
sub get_family {
my $group = shift;
if( $group eq 'flintstones' ){
qw/fred barney wilma dino/;
}elsif( $group eq 'jetsons' ){
qw/george jane judy elroy/;
}elsif( $group eq 'simpsons' ){
qw/homer marge bart lisa maggie/;
}
}
これを、次のループで利用すると、
foreach my $group ("simpsons", "jetsons", "flintstones" ) {
$HoA{$group} = [ get_family($group) ];
}
ハッシュ「 %HoA 」のデータ構造は次のようになります。
use Data::Dumper;
print Dumper \%HoA;
__END__
# %HoA のデータ構造
$VAR1 = {
'flintstones' => [
'fred',
'barney',
'wilma',
'dino'
],
'simpsons' => [
'homer',
'marge',
'bart',
'lisa',
'maggie'
],
'jetsons' => [
'george',
'jane',
'judy',
'elroy'
]
};
同様の処理は、次のように一時変数を利用して行うこともできます。
# likewise, but using temps
foreach my $group ( "simpsons", "jetsons", "flintstones" ){
my @members = get_family($group);
$HoA{$group} = [ @members ];
}
このコードでも、ハッシュ「 %HoA 」のデータ構造は上記と同じものになります。
[ 生成 ] 既存の配列のハッシュに要素を追加
既にハッシュの value としてセットされている無名配列に要素を追加するには、次のように「 @{ } 」によってハッシュの key を配列全体としてデリファレンスしつつ、演算子「 push 」を利用します。
# append new members to an existing family
push @{ $HoA{"flintstones"} }, "wilma", "betty";
[ アクセス ] 配列のハッシュの value にアクセス
ハッシュ「 %HoA 」の value にセットした無名配列のひとつの要素にアクセスするには、次の記法が利用可能です。
# one element
${$HoA{flintstones}}[0] = "Fred";
$HoA{flintstones}->[0] = "Fred";
$HoA{flintstones}[0] = "Fred";
これら 3 つの記法はすべて同じ要素を示します。デリファレンスの「 ${ } 」は「 -> 」に置き換えられること、意味が変わらなければ「 -> 」は省略可能だという規則によった記法です。
別の value にアクセスしたい場合は、ハッシュの key や配列の添字を操作します。
# another element
$HoA{simpsons}[1] =~ s/(\w)/\u$1/;
[ アクセス ] 配列全体を一度に出力
ハッシュ「 %HoA 」の value として格納した無名配列を配列全体として出力するには次のように記述します。
# print the whole thing
foreach my $family ( keys %HoA ){
print "family: @{ $HoA{$family} }\n";
}
配列全体としてデリファレンスしたいので、「 @{ } 」を利用します。
ハッシュの key と配列のインデクスを利用して無名配列の要素をすべて出力することもできます。
# print the whole thing with indices
foreach my $family ( keys %HoA ){
print "family: ";
foreach my $i ( 0 .. $#{ $HoA{$family} } ){
print " $i = $HoA{$family}[$i]";
}
print "\n";
}
このコードでは、「 family name 」を key とした value である無名配列の要素をインデクスによって個別に出力しています。
family: 0 = homer 1 = marge 2 = bart 3 = lisa 4 = maggie
family: 0 = fred 1 = barney 2 = wilma 3 = dino
family: 0 = george 1 = jane 2 = judy 3 = elroy
[ アクセス ] 配列の要素でソート
次のコードでは、演算子「 sort 」とスペースシップ演算子「 <=> 」(0x24a) を利用して配列の要素数でソートしています。
# print the whole thing sorted by number of memgers
foreach my $family ( sort { @{$HoA{$b}} <=> @{$HoA{$a}} } keys %HoA ){
print "$family: @{ $HoA{$family} }\n";
}
ここでは、「 sort 」のための特別な変数「 $a 」と「 $b 」を「 $b <=> $a 」で利用しているので、結果として降順 ( 要素数の多い順 ) で出力されます。例えば次のように。
simpsons: homer marge bart lisa maggie
jetsons: george jane
flintstones: fred
[ アクセス ] 配列の要素で多重ソート
ハッシュに格納した配列の要素数が同じだった場合のために (0x24e) で確認した「 多重ソート 」のテクニックも利用出来ます。
# print the whole thing sorted by number of members and name
foreach my $family ( sort {
# 要素数で 降順 sort
@{$HoA{$b}} <=> @{$HoA{$a}}
||
# または family name のアルファベットで 昇順 sort
$a cmp $b } keys %HoA )
{
print "$family: ", join(", ", sort @{ $HoA{$family} }), "\n";
}
このコードからは、例えば次の結果が得られます。
simpsons: bart, homer, lisa, maggie, marge
flintstones: barney, dino, fred, wilma
jetsons: elroy, george, jane, judy
まず、要素数の最も多い family「 simpsons 」が最初に出力されます ( 降順 )。次に要素数の同じ family が残りますが、family name のアルファベット順のソートによって「 flintstones 」が先に出力されています ( 昇順 )。
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)