Есть ли в Perl ассоциативный тип массива, который может иметь любой тип ключей? - PullRequest
5 голосов
/ 16 августа 2011

Кажется, что Perl может иметь только строки в качестве ключей хеша. (Было ли это изменено в определенной версии Perl?) Очень ограниченно иметь возможность использовать только строки в качестве ключа. Что если бы я хотел, чтобы ключом был объект или массив? В Python легко использовать массив, кортеж и другие объекты, которые могут сравниваться, в качестве ключей dict. В Perl есть возможность сравнивать такие вещи, как массивы, на равенство, поэтому я не понимаю, почему их нельзя использовать в качестве ключей типа отображения.

Нет ли способа использовать какой-либо тип ключа в Perl? Есть ли модуль, который обеспечивает это?

Ответы [ 4 ]

3 голосов
/ 16 августа 2011

Вопреки тому, что вы сказали, Perl НЕ имеет возможности сравнивать такие вещи, как массивы, на равенство, как вы заявляете.Для начала, в Perl нет определения равенства массивов.И если определение требует сравнения содержимого массива, то Perl не имеет определения равенства для большинства вещей, которые можно найти в массиве.

Ближайший Perl для определения массивов равенства:их адрес.Если это то, что вы хотите использовать, тогда это довольно просто:

$key = ['a', 'b'];
$hash{$key} = [ $key, $val ];   # Prevents the key from being freed.
print $hash{$key}[1];

В противном случае, Perl оставляет за вами право реализовывать то, что вы хотите, вместо того, чтобы заставлять вас использовать то, что он предоставляет.Я вижу два основных подхода.

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

Другим подходом может быть создание функции, которая генерирует уникальный ключ из выражения ключа.

sub key{ "@_" }  # Overly simplistic?
$hash{key('a', 'b')} = $val;
print $hash{key('a', 'b')};
3 голосов
/ 16 августа 2011

Да, для CPAN есть модуль для этого: Tie :: Hash :: StructKeyed . Он учитывает всю структуру, но в качестве снимка. каждая клавиша вернет исходную ссылку.

3 голосов
/ 16 августа 2011

AFAIK Perl просто строковые ключи?

package Foo;

use Moose;
use overload ('""' => \&stringify);

sub stringify {
    return "xxx";
}

package main;

use v5.10;
use Data::Dump 'pp';

my $foo = Foo->new;
my $hash = {$foo => 'bar'};
say pp($hash); // { xxx => "bar" }

Таким образом, вы также можете использовать в качестве хеш-ключа все, что захотите. Смотрите также эту тему на Perl Monks .

Что касается равенства, взгляните на операторов равенства в perlop . Оператор == сравнивает численно, оператор eq сравнивает по строкам. Это означает, что, например, (4, 2) == (1, 2) имеет значение true (поскольку scalar (4, 2) означает 2), что может вас удивить.

1 голос
/ 16 августа 2011

Да, используйте хеш-поля ( Hash :: Util :: FieldHash для 5.10+ и Hash :: Util :: FieldHash :: Compat для до 5.10), чтобы зарегистрировать свой хэши как хеши полей, и вы можете использовать любую ссылку (и, следовательно, любой объект) в качестве ключа для этих хешей (по сути, он нумерует ссылку и предоставляет логику для работы с CLONEing между потоками и сбора мусора ключей), например:

use Hash::Util qw/fieldhashes/;
fieldhashes \my (%foo, %bar, %baz);

my %data = (a => 1, b => 2, c => 3);
my $stuff = {t => 2, u => 3, l => 9};
$foo{\%data} = 'x';
$foo{$stuff} = 'a';
$bar{\%data} = 'y';
$bar{$stuff} = 'b';
$baz{\%data} = 'z';
$baz{$stuff} = 'c';


print "$foo{\%data} $bar{\%data} $baz{\%data}\n";
print "$foo{$stuff} $bar{$stuff} $baz{$stuff}\n";
...