2010-10-28 20 views
4

J'ai un paquet X.pm avec une méthode data_x();
J'utilise des instances de la classe X comme des clés d'un hachage %seen, dire.
Maintenant, les éléments de keys %seen semblent avoir oublié leur bénédiction:Perl: les clés de hachage ont perdu leur information de classe

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"; 

Cette impression:

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. 

Les deux $x et $y points à la même adresse, mais apparemment keys ne copie pas les informations de classe .
Pourquoi est-ce ainsi?

Répondre

7

Ils n'ont pas seulement perdu leur bénédiction, ils ne sont même plus des hashrefs.

Vous pouvez uniquement utiliser des chaînes comme clés de hachage en Perl. Tout ce qui n'est pas déjà une chaîne sera transformé en chaîne de caractères. Donc, la clé dans le hachage n'est plus un objet, mais la chaîne 'X = HASH (0x228fd48)' (qui ressemble à un hashref béni à l'impression). Il n'y a aucun moyen de récupérer l'objet de cette chaîne (sauf si vous avez un autre hachage qui mappe ces clés aux objets originaux).

Vous devez utiliser un identifiant unique comme clé de hachage à la place. Il semble que vous pouvez utiliser la version de chaîne courante (qui est essentiellement une adresse de mémoire) pour au moins vérifier l'identité de l'objet (l'objet ne semble pas bouger tant qu'il est vivant), mais je ne suis pas sûr de la stabilité être (certaines implémentations d'objets "inside-out" semblent être basées sur cette idée, cependant), et cela ne vous donne pas de contrôle d'égalité d'objet.

+0

Merci pour cela, Thilo – Klaus

+3

La bonne façon d'obtenir l'identifiant unique d'une référence est d'utiliser Scalar :: Util :: RefAddr http://search.cpan.org/~gbarr/Scalar-List- Utils/lib/Scalar/Util.pm – friedo

1

Seules les chaînes peuvent être utilisées comme clés de hachage. Lorsque vous insérez votre instance en tant que clé, elle est convertie en chaîne.

Options:

  • utiliser une chaîne qui peut également être utilisé pour construire l'instance appropriée
  • a un hachage de chaînes uniques à l'objet refs
  • sérialiser l'objet d'une chaîne, et restauration lorsque retiré

Votre meilleur pari est de maintenir un hachage d'identifiants de chaînes uniques aux objets refs. IMHO

1

En plus des autres commentaires posts, même si vous obtenez un identificateur d'objet unique, si vous ne créez pas de référence à cet objet ailleurs que dans la clé de hachage, l'objet peut tomber hors de portée, récupérer les ordures et devenir inaccessible.

Jetez un oeil à cet exemple de code et ce qu'il produit:

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->{$_}; 
    } 
} 

Sorties:

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) 

Vous verrez que chaque objet clé obtenu GC'd à la fin de la boucle en raison de ne plus y faire référence. Et vous verrez une chose amusante supplémentaire, que l'objet-clé que nous avons généré pour "4" utilise la même adresse mémoire que "1", donc lorsque nous avons remplacé sa valeur dans le hachage, la valeur était aussi GC'd. :/

La résolution de ce problème est assez simple, et voici une façon de le faire:

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->{$_}; 
    } 
} 

Sortie:

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

Le module Tie::RefHash norme fonctionne autour de la restriction que les clés de hachage sont stringifié.

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

Voilà une autre bonne idée, merci – Klaus