Perlにおけるハッシュ(連想配列)は、キーと値のペアを保存するための強力なツールです。しかし、同時に構造を反復および変更する必要がある場合は注意が必要です。ここでの細かい点は、診断が困難なエラーを引き起こすことがあります。
連想配列は、Perlの初期バージョン(Perl 1/2)で導入され、ハッシュのコアサポートを持つ最初の言語の一つとなりました。時が経つにつれ、eachを介した反復、削除(delete)、一括変換(map、grep)、および反復中のサイズ/内容の変更に対する対策が追加されてきました。
ハッシュを反復しながら同時にその内容を変更すること、特に要素を削除することは、予期しない効果を引き起こす可能性があります:要素のスキップ、同じキーの再処理、または無限ループになることもあります。さらに、キーの反復順序は保証されておらず、Perlのバージョン間で変わる可能性があります。
eachを使用している場合は、反復中にハッシュを変更しないでください;内部カーソルがリセットされるため。keysを使ってキーのリストを収集し、それに基づいて別のループで反復し削除します。while (my ($k, $v) = each %h)を使用し、ループ内部でdeleteを併用しないようにします。そうしないと予期しない事態が発生します。要素を正しく削除する例:
my %h = (a=>1, b=>2, c=>3); for my $k (keys %h) { delete $h{$k} if $h{$k} == 2; }
不適切なアプローチの例:
while (my ($k, $v) = each %h) { delete $h{$k}; # これによりキーがスキップされる可能性があります }
主要な特徴:
eachを使用した反復は、作業中の構造の変更に敏感です。while (each %h)のループ内でハッシュの要素を安全に削除できますか?
いいえ、これは内部反復カーソルのリセットによりハッシュの部分がスキップされる可能性があります。
ハッシュの要素を削除した後、キーの順序はどうなりますか?
順序は保証されておらず、変更される可能性があります。また、同じPerlを使用するプログラム間で反復順序が異なる場合があります。
eachを介して反復中にハッシュの要素の値を変更できますか?
はい、値の変更(ただし構造の変更ではない)は安全です。
例:
while (my ($k, $v) = each %h) { $h{$k} = $v + 10; }
each反復中に要素を直接削除することeachを介して要素を削除する場合:
my %h = (a=>1, b=>2, c=>3); while (my ($k, $v) = each %h) { delete $h{$k} if $v == 1; }
利点:
欠点:
削除するためのキーのリストを作成:
my %h = (a=>1, b=>2, c=>3); for my $k (keys %h) { delete $h{$k} if $h{$k} == 1; }
利点:
欠点: