オブジェクトをキーとしたハッシュ
Perl の組込みのハッシュは、いろいろな点で java.util.Map (Java) とか std::map (C++) と違うので、
同じような動作を期待するとはまることが多い。
Perl の組込みのハッシュは文字列をキーとしたハッシュなので、
bless されたリファレンスはたとえば 「クラス名=HASH(0xXXXXXX)」のような文字列として扱われる。
キーの同値性検査も、その文字列をつかって行われる。
したがって、
・アドレスが一致しないとキーが一致したとみなされない
・文字列からもとのオブジェクトへの参照はないので、ハッシュが生きていても、元のオブジェクトが破壊されていることがある。
そもそも、文字列からもとのオブジェクトを復元する手段がない。
対処はいくつかあって、
1. キーにしたいクラスで overload q{""} => sub {ユニークな表現を返す}
若干自由度が高くなる。
あまり深く考えたくないときは、シリアライズモジュール FreezeThawや Storableを使うと良さげ。
もとのオブジェクトを復元するのに、freeze, thaw を使う必要がある。
2. Tie::Hash::StructKeyed
自動的にリファレンスの中身をYAML形式にダンプしてキーに使う。
復元も可能。
$hash{yaml化されたキー} = [キー, 値]
を
$hash{キー} = 値
のように見せている。
アクセスのたびにyaml化をするので、けっこう重い。
3. 自作モジュール
std::tr1::unordered_map をラップしてPerlモジュール作成。
コンストラクタにハッシュ値関数と比較関数を渡すとかで。
でも、ハッシュ関数をPerlで書かせるのなら、
コード量の割に速度向上の恩恵が少ないような気がする。
2と3は、標準的なPerlだけでは動かないというのが問題といえば問題。
4. Tie::RefHash
常にアドレスの一致だけで同値性を判定し、文字列化しない(ようにみえる)ハッシュは
Tie::RefHashとして標準モジュールに入っているけれど、
最終的にはアドレス値を文字列として比較しなければならないのが、どうしても気持ち悪い。