2010-10-28 68 views
4

我有一个包X.pm与方法data_x();
我用X类的实例作为哈希%seen的钥匙,说。
现在的keys %seen元素似乎已经忘记了自己的祝福:的Perl:哈希键已经失去了他们的类信息

use X; 

my($x, $y, %seen); 

$x = X->new(); 
$x->data_x(1); 

print " x:  ", $x, "\n"; 
print " x.data: ", $x->data_x(), "\n"; 

$seen{ $x } = 1; 
$y = (keys %seen)[0]; 

print " y:  ", $y, "\n"; 
print " y.data: ", $y->data_x(), "\n"; 

此打印:

x:  X=HASH(0x228fd48) 
x.data: 1 
y:  X=HASH(0x228fd48) 
Can't locate object method "data_x" via package "X=HASH(0x228fd48)" 
(perhaps you forgot to load "X=HASH(0x228fd48)"?) at test.pl line 15. 

两个$x$y指向同一个地址,但显然keys没有复制类信息。
这是为什么?

回答

7

他们不仅失去了他们的祝福,他们甚至没有hashrefs了。

你只能在Perl中使用字符串作为散列键。

所有不是字符串的东西都会变成一个字符串。所以散列中的键不再是对象,而是字符串'X = HASH(0x228fd48)'(这是打印时的祝福hashref的样子)。没有办法从该字符串中取回对象(除非您有另一个将这些键映射到原始对象的散列)。

您需要使用唯一标识符作为散列键。看起来,你可以使用当前的字符串版本(基本上是一个内存地址)来至少检查对象的身份(对象似乎不会在它活着的时候被移动),但我不确定这会有多稳定(尽管内部对象的一些实现似乎基于这个想法),并且它不会给你对象相等性检查。

+0

感谢的是,蒂洛 – Klaus 2010-10-28 04:50:42

+3

正确的方式来获得一个参考的唯一标识符是使用标量::的Util :: refaddr http://search.cpan.org/~gbarr/Scalar-List- Utils/lib/Scalar/Util.pm – friedo 2010-10-28 04:55:30

1

只能使用字符串作为散列键。当您将实例作为密钥插入时,它将转换为字符串。

选项:

  • 使用,也可用于构建相应的实例
  • 一个字符串有唯一的字符串的散列反对裁判
  • 序列化对象的字符串,并恢复时拉出

最好的办法是保持唯一字符串id到对象引用的散列。恕我直言

1

除了其他帖子的评论,即使你得到一个独特的对象标识符,如果你没有创建一个引用,而不是在哈希键以外的地方,该对象可能会超出范围,收集垃圾,并变得无法访问。

看看这个代码示例,它会产生什么:

use strict; 
use warnings; 
$|++; 
{ 
    package X; 
    use Moose; 

    has side => (isa => 'Str', is => 'rw', required => 1); 
    has foo => (isa => 'Int', is => 'rw', required => 1); 

    sub DEMOLISH { 
     my ($self) = @_ ; 
     printf "Destroyed %i (%s)\n" , $self->foo, $self->side; 
    } 
    __PACKAGE__->meta->make_immutable; 
} 

{ 
    package Y; 

    my $hash = {}; 

    for (1 .. 5){ 
     print "Creating $_ \n"; 
     my $k = X->new(foo => $_ , side => 'key'); 
     my $v = X->new(foo => $_, side => 'value'); 

     $hash->{$k} = $v; 
     print "Created $_ at $k \n"; 
    } 

    for (keys %$hash){ 
     print "Emptying Hash slowly, doing key $_ \n"; 
     delete $hash->{$_}; 
    } 
} 

输出:

Creating 1 
Created 1 at X=HASH(0x2597d08) 
Destroyed 1 (key) 
Creating 2 
Created 2 at X=HASH(0x2fca7c0) 
Destroyed 2 (key) 
Creating 3 
Created 3 at X=HASH(0x2fca808) 
Destroyed 3 (key) 
Creating 4 
Destroyed 1 (value) 
Created 4 at X=HASH(0x2597d08) 
Destroyed 4 (key) 
Creating 5 
Created 5 at X=HASH(0x2597d68) 
Destroyed 5 (key) 
Emptying Hash slowly, doing key X=HASH(0x2597d68) 
Destroyed 5 (value) 
Emptying Hash slowly, doing key X=HASH(0x2597d08) 
Destroyed 4 (value) 
Emptying Hash slowly, doing key X=HASH(0x2fca808) 
Destroyed 3 (value) 
Emptying Hash slowly, doing key X=HASH(0x2fca7c0) 
Destroyed 2 (value) 

你会看到每一个关键对象拿到的结束GC'd因为那里没有任何提及它的循环。 你会看到一个额外的有趣的事情,我们为“4”生成的关键对象使用与“1”相同的内存地址,所以当我们在哈希中替换它的值时,该值也是GC'd。 :/

解决这个问题相当简单,这里是做这件事:

use strict; 
use warnings; 
$|++; 
{ 
    package X; 
    use Moose; 
    use Data::UUID; 

    my $ug = Data::UUID->new(); 

    has side => (isa => 'Str', is => 'rw', required => 1); 
    has foo => (isa => 'Int', is => 'rw', required => 1); 
    has uuid => (isa => 'Str', is => 'rw', required => 1 , builder => '_build_uuid'); 

    sub _build_uuid { 
     return $ug->create_str(); 
    } 
    sub DEMOLISH { 
     my ($self) = @_ ; 
     printf "Destroyed %i (%s , %s)\n" , $self->foo, $self->side, $self->uuid; 
    } 
    __PACKAGE__->meta->make_immutable; 
} 

{ 
    package Y; 

    my $hash = {}; 
    my $keys = {}; 

    for (1 .. 5){ 
     print "Creating $_ \n"; 
     my $k = X->new(foo => $_ , side => 'key'); 
     my $v = X->new(foo => $_, side => 'value'); 

     $keys->{$k->uuid} = $k; 
     $hash->{$k->uuid} = $v; 
     print "Created $_ at $k \n"; 
    } 

    for (sort keys %$hash){ 
     print "Emptying Hash slowly, doing key $_ \n"; 
     delete $hash->{$_}; 
     delete $keys->{$_}; 
    } 
} 

输出:

Creating 1 
Created 1 at X=HASH(0x2a12b58) 
Creating 2 
Created 2 at X=HASH(0x2a0d068) 
Creating 3 
Created 3 at X=HASH(0x2a28960) 
Creating 4 
Created 4 at X=HASH(0x2a28b28) 
Creating 5 
Created 5 at X=HASH(0x2a28c18) 
Emptying Hash slowly, doing key ADD9C702-E254-11DF-A4A3-F48B02F52B7F 
Destroyed 1 (value , ADD9CA18-E254-11DF-A4A3-F48B02F52B7F) 
Destroyed 1 (key , ADD9C702-E254-11DF-A4A3-F48B02F52B7F) 
Emptying Hash slowly, doing key ADD9CBD0-E254-11DF-A4A3-F48B02F52B7F 
Destroyed 2 (value , ADD9CCD4-E254-11DF-A4A3-F48B02F52B7F) 
Destroyed 2 (key , ADD9CBD0-E254-11DF-A4A3-F48B02F52B7F) 
Emptying Hash slowly, doing key ADD9CE5A-E254-11DF-A4A3-F48B02F52B7F 
Destroyed 3 (value , ADD9CF5E-E254-11DF-A4A3-F48B02F52B7F) 
Destroyed 3 (key , ADD9CE5A-E254-11DF-A4A3-F48B02F52B7F) 
Emptying Hash slowly, doing key ADD9D0DA-E254-11DF-A4A3-F48B02F52B7F 
Destroyed 4 (value , ADD9D1DE-E254-11DF-A4A3-F48B02F52B7F) 
Destroyed 4 (key , ADD9D0DA-E254-11DF-A4A3-F48B02F52B7F) 
Emptying Hash slowly, doing key ADD9D38C-E254-11DF-A4A3-F48B02F52B7F 
Destroyed 5 (value , ADD9D49A-E254-11DF-A4A3-F48B02F52B7F) 
Destroyed 5 (key , ADD9D38C-E254-11DF-A4A3-F48B02F52B7F) 
3

标准Tie::RefHash模块工作各地限购令的哈希键字符串化。

NAME 
    Tie::RefHash - use references as hash keys 

SYNOPSIS 
    use Tie::RefHash; 
    tie HASHVARIABLE, 'Tie::RefHash', LIST 
    tie HASHVARIABLE, 'Tie::RefHash::Nestable', LIST; 

    untie HASHVARIABLE; 

DESCRIPTION 
    This module provides the ability to use references as hash 
    keys if you first "tie" the hash variable to this module. 
    Normally, only the keys of the tied hash itself are 
    preserved as references; to use references as keys in 
    hashes-of-hashes, use Tie::RefHash::Nestable, included as 
    part of Tie::RefHash. 
+0

这是另一个伟大的提示,谢谢 – Klaus 2010-10-28 13:28:18