Perl:散列键已丢失其类信息

发布于 2024-09-29 02:26:16 字数 768 浏览 1 评论 0原文

我有一个包 X.pm ,其方法为 data_x();
比如说,我使用 X 类的实例作为哈希 %seen 的键。
现在 keys %seen 的元素似乎忘记了它们的祝福:

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 没有复制类信息。
为什么会这样呢?

I have a package X.pm with a method data_x();
I use instances of class X as keys of a hash %seen, say.
Now the elements of keys %seen seem to have forgotten their blessing:

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

This prints:

 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.

Both $x and $y point to the same address, but apparently keys did not copy the class info.
Why is that so?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

↙厌世 2024-10-06 02:26:16

他们不仅失去了祝福,甚至不再是哈希引用了。

在 Perl 中只能使用字符串作为哈希键。

所有还不是字符串的东西都会被变成字符串。因此,散列中的键不再是对象,而是字符串“X=HASH(0x228fd48)”(这就是打印时受祝福的 hashref 的样子)。无法从该字符串中获取对象(除非您有另一个将这些键映射到原始对象的散列)。

您需要使用唯一标识符作为哈希键。看来您可以使用当前的字符串版本(基本上是一个内存地址)来至少检查对象标识(该对象在其存活时似乎不会移动),但我不确定这有多稳定be(不过,由内而外对象的某些实现似乎基于这个想法),并且它不会为您提供对象相等性检查。

They did not only lose their blessing, they are not even hashrefs anymore.

You can only use strings as hash keys in Perl.

Everything that is not already a string will be made into a string. So the key in the hash is not an object anymore, but the string 'X=HASH(0x228fd48)' (which is what a blessed hashref looks like when printed). There is no way to get the object back from that string (unless you have another hash which maps these keys to original objects).

You need to use a unique identifier as the hash key instead. It appears that you can use the current string version (which is basically a memory address) to at least check for object identity (the object does not seem to be moved around while it is alive), but I am not sure how stable that would be (some implementation of inside-out objects seem to be based on this idea, though), and it does not give you object equality checks.

烟凡古楼 2024-10-06 02:26:16

标准 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.

The standard Tie::RefHash module works around the restriction that hash keys are stringified.

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.
夜血缘 2024-10-06 02:26:16

除了其他帖子评论之外,即使您确实获得了唯一的对象标识符,如果您不在哈希键之外的其他位置创建对该对象的引用,该对象可能会超出范围,被垃圾收集,并且变得无法访问。

看一下此代码示例及其生成的内容:

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 了,因为不再有任何对其的引用。
你会看到一个额外有趣的事情,我们为“4”生成的键对象使用与“1”相同的内存地址,所以当我们在哈希中替换它的值时,该值也被GC了。 :/

解决这个问题相当简单,这是一种方法:

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 )

In addition to the other posts comments, even if you do get a unique object identifer, if you don't create a reference to that object somewhere other than in the hash key, the object might fall out of scope, get garbage collected, and become inaccessable.

Take a look at this code sample and what it produces:

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

Outputs:

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 )

You'll see that every single key object got GC'd at the end of the loop due to there no longer being any reference to it.
And you'll see an additional fun thing, that the key-object we generated for "4" used the same memory address as "1", so when we replaced its value in the hash, the value was also GC'd. :/

Solving this issue is reasonably simple, and here is one way to do it:

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

Output:

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 )
对你而言 2024-10-06 02:26:16

只有字符串可以用作哈希键。当您插入实例作为键时,它会转换为字符串。

选项:

  • 使用也可用于构造适当实例的字符串
  • 具有对象引用的唯一字符串的哈希值将
  • 对象序列化为字符串,并在拉出时恢复

您最好的选择是维护对象的唯一字符串 ID 的哈希值参考文献。恕我直言

Only strings can be used as hash keys. When you insert your instance as the key it is converted to a string.

Options:

  • use a string that can also be used to construct the appropriate instance
  • has a hash of unique strings to object refs
  • serialize the object to a string, and restore when pulled out

Your best bet is to maintain a hash of unique string ids to object refs. IMHO

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文