Объект как хэш-ключ

Можно ли использовать объект как хэш-ключ?

Например, следующий код позволяет мне использовать экземпляр MyClass в качестве ключа, но когда я перебираю ключи и пытаюсь вызвать метод get_value, я получаю ошибку:

Невозможно найти метод объекта get_value через пакет "Myclass= HASH (0x12a4040)" (возможно, вы забыли загрузить "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;
}

Ответ 1

По умолчанию все хеш-ключи в Perl являются строками, поэтому то, что происходит в вашем коде (что имеет и другие проблемы), заключается в том, что вы конвертируете объект в строку и сохраняете строку.

В общем случае, если вы хотите использовать объект в качестве ключа, самый простой способ сделать это - использовать две структуры данных: одну, которая содержит ваши объекты (массив), и другую, которая сопоставляет объекты некоторым значениям ( хэш). Также возможно создать привязанный хэш, который будет поддерживать объекты как ключи, но в целом привязанные хеши будут медленнее, чем просто использовать две структуры данных.

Стандартный модуль Tie:: RefHash предоставляет механизм для использования объектов (и других ссылок) в качестве хеш-ключей (которые работают правильно, когда вы получаете их обратно).

use Tie::RefHash;
tie my %hash, 'Tie::RefHash';

$hash{MyClass->new(1)} = 0;  # never use the indirect object syntax
....

Ответ 2

Все, что используется как хэш-ключ, стягивается. Поэтому, когда вы используете свой объект как хэш-ключ, вы получаете только строковое представление, а не фактический объект.

Реальный вопрос: зачем в мире вы хотели бы это сделать?

Кроме того, синтаксис присваивания значений хешу равен $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();

Если вы хотите, чтобы ваши объекты могли возвращать уникальный уникальный идентификатор для каждого экземпляра, вы можете добавить метод, который использует оператор Scalar:: Util refaddr:

use Scalar::Util 'refaddr';

sub unique_id { 
    my $self = shift;
    return refaddr $self;
}

...

$hash{MyClass->new(1)->unique_id} = 0;

Подробнее: perlobj, perldata, perlreftut, Scalar::Util