是否可以使用对象作为哈希键?
例如,以下代码允许我使用 MyClass 的实例作为键,但是当我迭代这些键并尝试调用
get_value
方法时,我收到错误:
无法通过包“MyClass=HASH(0x12a4040)”找到对象方法“get_value”(也许您忘记加载“MyClass=HASH(0x12a4040)”?)
package MyClass;
use strict;
sub new
{
my $class = shift;
my $self = {
_value => shift
};
bless $self, $class;
return $self;
}
sub get_value {
my($self) = @_;
return $self->{_value};
}
my %hash = ();
%hash->{new MyClass(1)} = 0;
%hash->{new MyClass(2)} = 1;
for my $key (keys %hash)
{
print $key->get_value;
}
默认情况下,Perl 中的所有哈希键都是字符串,因此您的代码中发生的情况(也存在其他问题)是将对象转换为字符串并存储该字符串。
一般来说,如果您想使用对象作为键,最简单的方法是使用两种数据结构,一种保存对象(数组),另一种将对象映射到某些值(哈希) )。还可以创建一个支持对象作为键的绑定哈希,但一般来说,绑定哈希会比简单使用两个数据结构慢。
标准模块 Tie::RefHash 提供了一种使用对象(和其他引用)作为哈希键的机制(当您取回它们时,它们可以正常工作)。
use Tie::RefHash;
tie my %hash, 'Tie::RefHash';
$hash{MyClass->new(1)} = 0; # never use the indirect object syntax
....
任何用作哈希键的东西都会被字符串化。因此,当使用对象作为哈希键时,您只会获得它的字符串表示形式,而不是实际的对象本身。
真正的问题是,你到底为什么要这样做?
此外,为哈希值赋值的语法是
$hash{key} = $val
;当您处理哈希引用时使用箭头。
如果您想将对象与其他值关联起来,一种方法是使用哈希数组,例如
my @foo;
push @foo, { obj => MyClass->new( 1 ), val => 0 };
push @foo, { obj => MyClass->new( 2 ), val => 1 };
然后你可以打电话
$foo[0]{obj}->get_value()
;
如果您只是希望对象能够返回一些唯一的每个实例 ID,您可以添加一个利用 Scalar::Util 的
refaddr
运算符的方法:
use Scalar::Util 'refaddr';
sub unique_id {
my $self = shift;
return refaddr $self;
}
...
$hash{MyClass->new(1)->unique_id} = 0;
对于使用 convert_blessed
我们正在序列化使用 JSON 的 TO_JSON 挂钩进行字符串化的对象,并返回一个哈希对象:
sub TO_JSON {
return { a => { b => 'c'}, d => [1, 2, 3] }
}
之后,使用
JSON::to_json
进行字符串化:
my $json_string = JSON::to_json($object_from_above, { convert_blessed => 1 });
Perl 不一致地重新排序
a
和 d
键,这使得字符串化对象上的散列不可靠。
canonical
选项,如下所示:
my $json_string = JSON::to_json($object_from_above, {
convert_blessed => 1,
canonical => 1 # the fix
});
注意:大对象将有巨大的 JSON 键,所以要小心...