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

セラ (perlackline)

2016年03月11日 12:48



目次 - 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 」のデータ構造は次のようになります。


関連記事