かなり前にオセロみたいなゲームを作ろうと思ったことを思い出して、せっせと作り始める。
そんなわけでPerlでオブジェクト指向っぽくプログラミングをしていたら、
どうしてもcloneっぽい機能がほしくなった。
正しいかわからないけど、cloneっぽく機能するメソッドを書いてみた。
実はcloneってのが何をやっているかよくわかってない。
リファレンスを芋づる式みたいに辿ってそのコピーをとるんでしょって認識で合っているのなら、
要は、参照ならその参照先もコピーすればいいってことなので、
再帰させれば簡単にできるんじゃないかな。と。
package xxx; # 〜 # クローンメソッド sub clone{ my $self = shift; my $clone = bless (_recursiveClone($self), __PACKAGE__); print "clone:$self => $clone\n"; foreach(keys %{$self}){ print " clone{$_}:$self->{$_} =>$clone->{$_}\n"; } return $clone; } # 再帰 sub _recursiveClone{ my $ref = shift; if(ref $ref eq 'HASH' || ref $ref eq __PACKAGE__){ my %hash = (); foreach(keys %$ref){ $hash{$_} = _recursiveClone($ref->{$_}); } return \%hash; }elsif(ref $ref eq 'ARRAY'){ my @array = (); foreach(@$ref){ push(@array, _recursiveClone($_)); } return \@array; }elsif(ref $ref eq 'SCALAR'){ my $tmp = $$ref; return \$tmp; }elsif(ref $ref eq 'REF'){ my $tmp = _recursiveClone($$ref); return \$tmp; } return $ref; }
cloneメソッドのbless (_recursiveClone($self), __PACKAGE__);ってところは、
表示確認のためにblessしたものを一回受け取ってるだけなので、そのままreturnしても問題ないはず。
_recursiveCloneは受け取った変数が何のリファレンスか調べて、
決まったリファレンスであれば、それぞれにあったデリファレンスをしていき、
さらに_recursiveCloneに放り込み、最終的にリファレンスを返す。
ただ、スカラーのリファレンスだけはデリファレンスした後、代入した変数の参照を返す。
リストかハッシュかスカラーのリファレンスでないなら、そのまま返す。
CODEは結局は同じ関数を参照するのだから、デリファレンスの必要はないと思う。
GLOBとかは使わないから良くわかんない。無くてもとりあえず困らない。
あとココによると他にもリファレンスの種類があるけど、
今までPerlを使ってきて見たこと無いので、大丈夫だろう。
__PACKAGE__と比較しているのは、blessされた変数をrefすると、パッケージ名を返すので、
今回使用しているパッケージのコンストラクタはhashをblessするので、
とりあえずhashと同じ扱いで展開しています。