ハッシュの配列をdeep copyする


例えばこんなハッシュの配列の参照をコピーする場合、どうするの?

my $hashArray = [
	{"aaa"=>1},
	{"aaa"=>2},
	{"aaa"=>3},
	{"aaa"=>4}
];

先に結論

自力でやるなら、配列の中身を一個一個実体コピー
もしくは、Cloneモジュールを使う

use Clone qw(clone);

# 自力の場合
my $hashArrayCopy5 = [map {{%$_}} @$hashArray];
# cloneモジュールを使う場合
my $hashArrayCopy7 = clone($hashArray);

実装

案1(shallow copy)

これだと、shallow copy

my $hashArrayCopy1 = $hashArray;
案2(shallow copy)

@$〜で、配列のリファレンスを実体化(デリファレンス)できるので、配列の実体をコピー
だけど、配列の中身は結局、ハッシュのリファレンスなので、
やっぱり、shallow copy

my @hashArrayCopy2 = @$hashArray;	# @〜は、リファレンスの実体化
案3(shallow copy)

mapを使うが、この場合も結局案2と同じなのでNG
mapは、以下の 式 or ブロックの処理を
全てのリストの要素で行って、結果を配列で返す関数

  • map 式, リスト
  • map { ブロック }, リスト
my $hashArrayCopy3 = [map{$_}(@$hashArray)];


ちなみに、配列の配列の場合は、mapを使って、deep copy可能

my $arrayArray = [
	[1, 2, 3],
	[4, 5, 6],
	[7, 8, 9]
];
my $arrayArrayCopy = [map[@{$_}], @$arrayArray];

$arrayArray->[0]->[0] = 999999;
# 0 と表示
print $arrayArrayCopy->[0]->[0];
案4(deep copy)

配列の中身をforeachして、中身を実体でコピー

my $hashArrayCopy4 = [];
foreach (@$hashArray){
	my %buf = %$_;	# 一度実体にコピー
	push(@$hashArrayCopy4, \%buf);
}
案5(deep copy)

もっとスマートな方法

my $hashArrayCopy5 = [map {{%$_}} @$hashArray];


mapでは、{%$_}で、配列の参照を実体化できるが、中括弧"{"はブロック扱いになるので、以下はNG
配列の中のハッシュの全ての Key と Value を値にもった1次元配列が、$hashArrayCopy6に格納される

my $hashArrayCopy6 = [map {%$_} @$hashArray];
案6(deep copy)

Cloneモジュールを使う

use Clone qw(clone);
my $hashArrayCopy7 = clone($hashArray);


ちなみに、cloneメソッドは、第二引数でdeep copyの深度を指定できる

参考

その他

検証用コードはこちら(SJISです)
http://kasei-san.sakura.ne.jp/rabo/copyTest.pl


perlの配列、ハッシュのリファレンスは大体理解
他のLLと違って、わざわざ明示的にリファレンスにしたのはなんでなんだろ?
元々リファレンスが古いバージョンでは存在しなくて、全てディープコピーで多次元配列が作れなかったのを
途中のバージョンアップで、実装したのでなんかややこしくなったとかな気がする…。