2010-10-28 69 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