开发者

Does Perl have an associative array type that can have any type of keys?

It seems that Perl can only have strings as the keys of a hash. (Has this been changed in a certain version of Perl?) It is very limiting to only be able to use strings as the key. What if I wanted an object or an array to be the key? In Python it is easy to use an array, tuple, and other objects that can compare, as dict keys. Perl does have the ability to compare things like arrays for equality, so I don't see why they can't be used as mapping-type keys.

Isn't there a way to use any key type in P开发者_如何学运维erl? Is there a module that provides this?


Contrary to what you said, Perl does NOT have the ability to compare things like arrays for equality as you claim. For starters, Perl has no definition for array equality. And if the definition requires comparing the contents of the array, then Perl doesn't have definitions of equality for most things that can be found in an array either.

The closest Perl has for a definition of equality arrays is their address. If that's what you want to use, then it's quite easy:

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

Otherwise, Perl leaves it up to you to implement what you want instead of forcing you to use what it provides. I see two main approaches.

A tied hash, basically code that presents the interface of a hash without actually being a hash table, can support any key type. You could use it to define your version of array equality. There might even be an existing module (although I didn't see one after a very quick search).

Another approach would be to create a function that produces a unique key from the key expression.

sub key{ "@_" }  # Overly simplistic?
$hash{key('a', 'b')} = $val;
print $hash{key('a', 'b')};


AFAIK Perl simply stringifies the keys?

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

This way you can also use whatever you want as a hash key. See also this thread on Perl Monks.

As for the equality, take a look at equality operators in perlop. The == operator compares numerically, the eq operator compares stringwise. This means that for example (4, 2) == (1, 2) is true (as scalar (4, 2) is 2), which might be a surprise for you.


Yes, there is module for this on CPAN: Tie::Hash::StructKeyed. It takes into account whole structure, but as a snapshot. each/keys will return original reference.


Yes it does, use fieldhashes (Hash::Util::FieldHash for 5.10+ and Hash::Util::FieldHash::Compat for before 5.10) to register your hashes as fieldhashes, and you can use any reference (and thus, any object) as a key for those hashes (it numifies the reference, essentially, and provides logic for dealing with CLONEing across threads and garbage collecting the keys), for example:

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜