Perl Perl_5 リファレンス
Perl リファレンス 14 データ構造 ハッシュの配列, クロージャ (d017)

目次 - Perl Index
Theme
Perl について、復習を兼ねて断片的な情報を掲載して行く連載その d017 回。
Perl で、perldsc - perldoc.jp で解説されているデータ構造から「 ハッシュの配列 」の生成とアクセスの方法を確認する。
・ ハッシュの配列
・ [ 生成 ] ファイルからの読み込みによる生成
・ [ 生成 ] サブルーチンの呼び出しによる生成 + クロージャ
・ [ 生成 ] 既存のハッシュの配列に key/value を追加
・ [ アクセス ] ハッシュの配列 value にアクセス
・ [ アクセス ] ハッシュ全体を一度に出力
・ [ アクセス ] ハッシュ key/value を個別に出力
ハッシュの配列
「 ハッシュの配列 」( Array of Hash : AoH ) は次のような構造です。
@AoH = (
{
key00 => "val00",
key01 => "val01",
},
{
key10 => "val10",
key11 => "val11",
key12 => "val12",
},
{
key20 => "val20",
key21 => "val21",
}
);
[ 生成 ] ファイルからの読み込みによる生成
外部ファイルを読み込んで、その各行をハッシュ key/value として配列に格納する方法を確認します。
# reading from file
# format: LEAD=fred FRIEND=barney
while( <> ){
my $rec = {};
foreach my $field ( split ){
my ($key, $value) = split /=/, $field;
$rec->{$key} = $value;
}
push @AoH, $rec;
}
このコードではまず、無名ハッシュコンストラクタ「 { } 」を格納した一時変数「 $rec 」を用意します。
それから、ファイルの行を格納した暗黙の変数「 $_ 」を「 split 」で分割 ( \s+ 分割 ) します。
この時、「 $_ 」にはファイルから読み込んだ行「 LEAD=xxx FRIEND=xxx 」が格納されています。
これを「 split 」で分割するので、foreach の制御変数「 $field 」には最初のループで「 LEAD=xxx 」が、次のループで「 FRIEND=xxx 」が格納されます。
ループ内では、再び「 split 」を利用して「 $field 」を「 = 」で分割し、それを、スカラ「 $key 」と「 $value 」に格納します。
「 $key 」はハッシュ key としてデリファレンス「 $rec->{$key} 」に利用し、「 $value 」はその value として利用します。
これを行が終わるまで繰り返し、最後にハッシュリファレンスを格納した「 $rec 」を配列「 @AoH 」に push して「 ハッシュの配列 」の生成は完了です。
同様の処理は、次のように一時変数なしでも記述できます。
# reading from file
# format:LEAD=fred FRIEND=barney
# no temp
while( <> ){
push @AoH, { split /[\s+=]/ };
}
ここでは、「 split 」の PATTERN に「 [ ] 」, 「 \s 」, 「 = 」を指定しています。
これにより行「 LEAD=xxx FRIEND=xxx 」が、一度の「 split 」で「 LEAD xxx FRIEND xxx 」になります。
これを無名ハッシュコンストラクタ「 { } 」で囲むことで、ひとかたまりの無名ハッシュ「 LEAD => xxx, FRIEND => xxx 」に変換し、そのまま配列の要素として「 push 」するので、結果としてハッシュの配列が生成できます。
コードがとてもコンパクトになっていますが、少し技巧的過ぎるかもしれません。
[ 生成 ] サブルーチンの呼び出しによる生成 + クロージャ
サブルーチンの戻り値を無名ハッシュとして配列に格納する方法を確認します。
# calling a function that returns a key/value pair list, like
# "lead", "fred", "daughter", "pebbles"
while( my %fields = getnextpairset() ){
push @AoH, { %field };
}
リストを返すサブルーチンの処理は PerlPhrasebook - Python を参考にして次のように書くことで意図した動作が得られました。
my $counter = 0;
my @pairset = ([qw/LEAD bellri FRIEND noredo/],
[qw/LEAD luin FRIEND manny/],
[qw/LEAD klim FRIEND mick/],
);
sub getnextpairset {
if($counter > $#pairset){ return; }
return @{$pairset[$counter++]};
}
サブルーチン「 getnextpairset 」の戻り値は少し工夫をしないと while が無限ループしてしまうので注意が必要です。
しかしながら、ここでは「 $counter 」と「 @pairset 」がグローバル変数になっているので、(0x26c) を見直しつつサブルーチンを「 クロージャ 」( closure ) として次のように書き直しました。
sub getnextpairset {
# パラメータ設定
my $counter = 0;
my @pairlist = ([qw/LEAD bellri FRIEND noredo/],
[qw/LEAD luin FRIEND manny/],
[qw/LEAD klim FRIEND mick/],
);
# 無名サブルーチン
sub {
if($counter > $#pairlist){ return (); }
return @{$pairlist[$counter++]};
}
}
# 無名サブルーチン ( コードレフ ) を取得
my $getpair = getnextpairset();
# 「 $getpair->() 」でデリファレンスしてコードレフを利用
my @AoH;
while( my %fields = $getpair->()){
push @AoH, { %fields };
}
結果的に配列「 @AoH 」のデータ構造は次のようになります。
use Data::Dumper;
print Dumper \@AoH;
__END__
$VAR1 = [
{
'FRIEND' => 'noredo',
'LEAD' => 'bellri'
},
{
'FRIEND' => 'manny',
'LEAD' => 'luin'
},
{
'FRIEND' => 'mick',
'LEAD' => 'klim'
}
];
ついでに、コードリファレンス ( コードレフ ) を格納した「 $getpair 」の構造も確認しておきます。
use B::Deparse;
my $bd = B::Deparse->new;
print $bd->coderef2text($getpair);
__END__
{
use warnings;
use strict 'refs';
if ($counter > $#pairlist) {
return +();
}
return @{$pairlist[$counter++];};
}
一時変数を利用せずに、サブルーチンの戻り値を無名ハッシュとして直接配列に格納することも可能です。
# likewise, but using no temp vars
while ( <> ){
push @AoH, { parsepairs($_) };
}
ここでは、外部ファイルから読み込んだ行をサブルーチン「 parsepairs() 」で解析 ( parse ) してその戻り値を key/value のペアとして配列「 @AoH 」に格納しています。
[ 生成 ] 既存のハッシュの配列に key/value を追加
配列に格納された既存のハッシュに新たな key を追加するには、次のようにデリファレンスを行います。
# add key/value to an element
$AoH[0]{pet} = "dino";
$AoH[2]{pet} = "santa's little helper";
これは、配列の要素に格納されたハッシュリファレンスをデリファレンスしています。つまり、「 ${ } 」や「 -> 」を利用した次の記法と同じです。
${$AoH[0]}{pet} = "dino";
$AoH[0]->{pet} = "dino";
[ アクセス ] ハッシュの配列 value にアクセス
配列に格納したいずれかの要素、つまり、ハッシュの value にアクセスするには、前項の手法と同じです。
# one element
$AoH[0]{lead} = "fred";
他の要素 ( ハッシュ value ) へのアクセスも同様です。
# another element
$AoH[1]{lead} =~ s/(\w)/\u$1/;
[ アクセス ] ハッシュ全体を一度に出力
配列「 @AoH 」の要素として格納したハッシュ全体を一度に出力するには次のように記述します。
# print the whole thing with refs
foreach my $href ( @AoH ){
print "{ ";
foreach my $role ( keys %$href ){
print "$role=$href->{$role} ";
}
print "}\n";
}
このコードでは、例えば次のような出力が得られます。
{ FRIEND=noredo LEAD=bellri }
{ FRIEND=manny LEAD=luin }
{ FRIEND=mick LEAD=klim }
同様の処理では、配列のインデクスも利用可能です。
# print the whole thing with indices
foreach my $i ( 0..$#AoH ){
print "$i is { ";
foreach my $role ( keys %{ $AoH[$i] } ){
print "$role=$AoH[$i]{$role} ";
}
print "}\n";
}
このコードでは、例えば次のような出力が得られます。
0 is { FRIEND=noredo LEAD=bellri }
1 is { FRIEND=manny LEAD=luin }
2 is { FRIEND=mick LEAD=klim }
[ アクセス ] ハッシュ key/value を個別に出力
ハッシュの key/value を個別に出力するには次のように記述します。
# print the whole thing one at a time
foreach my $i ( 0..$#AoH ){
foreach my $role ( keys %{ $AoH[$i] } ){
print "elt $i $role is $AoH[$i]{$role}\n";
}
}
これにより、例えば次のような出力が得られます。
elt 0 FRIEND is noredo
elt 0 LEAD is bellri
elt 1 FRIEND is manny
elt 1 LEAD is luin
elt 2 FRIEND is mick
elt 2 LEAD is klim
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)