Your initial request immediately made me think of the lisp
function called rassq, which operates on an alist -- which
is very similar to a hash but implemented differently;
because of the way Perl's hashes are implemented, forward
lookups are very fast, but reverse lookups are relatively
expensive (as you've discovered). With alists, forward
and reverse lookups are roughly equally expensive.
It is relatively easy to implement an alist in Perl.
(The following code is untested...
my @alist = (
[key1 => value1],
[key2 => value2],
[key3 => value3],
);
sub assq {
my ($alistref, $key) = @_;
for (@$alistref) {
return $_ if $$_[0] eq $key;
# eq doesn't mean exactly the same thing as in lisp,
# but don't worry about that if you don't know lisp.
}
}
sub rassq {
my ($alistref, $value) = @_;
for (@$alistref) {
return $_ if $$_[1] eq $value;
# eq doesn't mean exactly the same thing as in lisp,
# but don't worry about that if you don't know lisp.
}
}
If you are working with large alists, assq will be much
slower than the equivalent hash lookup. You probably
can improve it slightly be replacing foreach with an
ugly C-style for loop.
I also don't know whether rassq will be significantly
faster than doing the equivalent thing on a hash; what
I do know is that it will be just as fast as assq.
If you want to compare the alist rassq with the
equivalent reverse hash lookup, I suggest using
Benchmark. Here's the closest thing I can come up
with for doing a rassq-like operation on a hash:
sub hashrassq {
my ($hashref, $value) = @_;
foreach (keys %$hashref) {
return $_ if $$hashref{$_} eq $value;
}
}
Also note that there are significant differences if more
than one hash key has the same value; the alist
implementation will always return the first one as
defined in terms of the order in which the associations
were added to the list (unless you add by unshifting
or similar), but the hashrassq will pick one (for
practical purposes) arbitrarily. You can define an
order by sorting the key list for the foreach loop, but
this does not allow you to take the first one added to
the hash, since hashes don't store order like that.
Also note that the hash implementation may be more
efficient if you iterate over the hash using each, just
as the alist may be more efficient if you iterate by
index. Only way to be sure is to use Benchmark.
|