2 Stimmen

Verwendung einer 'Unterprogramm-Referenz' als Hash-Schlüssel

In Perl lerne ich gerade, wie man "Subroutinenreferenzen" dereferenziert. Aber ich kann anscheinend keine Unterprogrammreferenz als Hash-"Schlüssel" verwenden.

Im folgenden Beispielcode,

  1. Ich kann einen Verweis auf ein Unterprogramm erstellen ($subref) und ihn dann dereferenzieren, um das Unterprogramm auszuführen (&$subref)
  2. Ich kann den Verweis als Hash-"Wert" verwenden und diesen dann einfach dereferenzieren
  3. Aber ich kann nicht herausfinden, wie ich die Referenz als Hash-Schlüssel verwenden kann. Wenn ich den Schlüssel aus dem Hash ziehe, interpretiert Perl den Schlüssel als String-Wert (nicht als Referenz) - was ich jetzt verstehen. (dank dieser Website!). Also habe ich versucht, Hash::MultiKey, aber das scheint es in ein Array-Referenz zu machen. Ich möchte es als eine Subroutine/Code-Referenz zu behandeln, vorausgesetzt, dies ist irgendwie möglich?

Haben Sie eine andere Idee?

use strict;
#use diagnostics;
use Hash::MultiKey;    

my $subref = \&hello;

#1: 
&$subref('bob','sue');               #okay

#2:
my %hash;
$hash{'sayhi'}=$subref;
&{$hash{'sayhi'}}('bob','sue');      #okay

#3: 
my %hash2;
tie %hash2, 'Hash::MultiKey';
$hash2{$subref}=1;
foreach my $key (keys %hash2) {
  print "Ref type is: ". ref($key)."\n";
  &{$key}('bob','sue');              # Not okay 
}

sub hello {
    my $name=shift;
    my $name2=shift;
    print "hello $name and $name2\n";
}

Dies wird zurückgesendet:

hello bob and sue
hello bob and sue
Ref type is: ARRAY
Not a CODE reference at d:\temp\test.pl line 21.

2voto

daxim Punkte 38607

Das ist richtig, ein normaler Hash-Schlüssel ist nur eine Zeichenkette. Dinge, die keine Strings sind, werden in ihre String-Darstellung gezwungen.

my $coderef = sub { my ($name, $name2) = @_; say "hello $name and $name2"; };
my %hash2 = ( $coderef => 1, );
print keys %hash2;  # 'CODE(0x8d2280)'

Krawatte ist das übliche Mittel zur Änderung dieses Verhaltens, aber Hash::MultiKey hilft Ihnen nicht weiter, es hat einen anderen Zweck: Wie der Name schon sagt, können Sie mehrere Schlüssel haben, aber wiederum nur einfache Zeichenketten:

use Hash::MultiKey qw();
tie my %hash2, 'Hash::MultiKey';
$hash2{ [$coderef] } = 1;
foreach my $key (keys %hash2) {
    say 'Ref of the key is: ' . ref($key);
    say 'Ref of the list elements produced by array-dereferencing the key are:';
    say ref($_) for @{ $key }; # no output, i.e. simple strings
    say 'List elements produced by array-dereferencing the key are:';
    say $_ for @{ $key }; # 'CODE(0x8d27f0)'
}

Verwenden Sie stattdessen Tie::RefHash . (Code-Kritik: Bevorzugen Sie diese Syntax mit dem -> Pfeil für die Dereferenzierung eines Coderefs).

use 5.010;
use strict;
use warnings FATAL => 'all';
use Tie::RefHash qw();

my $coderef = sub {
    my ($name, $name2) = @_;
    say "hello $name and $name2";
};

$coderef->(qw(bob sue));

my %hash = (sayhi => $coderef);
$hash{sayhi}->(qw(bob sue));

tie my %hash2, 'Tie::RefHash';
%hash2 = ($coderef => 1);
foreach my $key (keys %hash2) {
    say 'Ref of the key is: ' . ref($key);   # 'CODE'
    $key->(qw(bob sue));
}

1voto

Dave Cross Punkte 65470

Von perlfaq4 :

Wie kann ich eine Referenz als Hash-Schlüssel verwenden?

(Beitrag von Brian D. Foy und Ben Morrow)

Bei Hash-Schlüsseln handelt es sich um Zeichenketten, so dass Sie nicht wirklich eine Referenz als Schlüssel verwenden können. Wenn Sie das versuchen, verwandelt Perl den Verweis in seine stringifizierte Form (zum Beispiel HASH(0xDEADBEEF) ). Von dort aus kann man nicht zurückkehren die Referenz nicht aus der verketteten Form zurückholen, zumindest nicht ohne einige ohne zusätzliche eigene Arbeit.

Denken Sie daran, dass der Eintrag im Hash auch dann noch vorhanden ist, wenn die referenzierte Variable den Gültigkeitsbereich verlässt, und dass es durchaus möglich ist möglich ist, dass Perl später eine andere Variable an derselben Adresse Adresse zuzuweisen. Das bedeutet, dass eine neue Variable versehentlich mit dem Wert einer alten Variable verknüpft werden.

Wenn Sie Perl 5.10 oder eine neuere Version haben und nur einen Wert speichern wollen gegen die Referenz speichern wollen, um später nachzuschlagen, können Sie das Kernmodul Hash::Util::Fieldhash-Modul verwenden. Dieses wird auch die Umbenennung der Schlüssel behandeln wenn Sie mehrere Threads verwenden (was dazu führt, dass alle Variablen an neuen Adressen neu zugewiesen werden, wodurch sich ihre Stringifizierung ändert), und Müllsammeln der Einträge, wenn die referenzierte Variable den aus dem Geltungsbereich.

Wenn Sie tatsächlich einen echten Schiedsrichter brauchen Hash-Eintrag zu erhalten, können Sie das Modul Tie::RefHash verwenden, das die erforderliche Arbeit für Sie erledigt.

Es sieht also so aus Tie::RefHash wird tun, was Sie wollen. Aber um ehrlich zu sein, glaube ich nicht, dass das, was Sie tun wollen, eine besonders gute Idee ist.

0voto

choroba Punkte 214897

Warum brauchen Sie es? Wenn Sie z. B. Parameter für die Funktionen im Hash speichern müssen, können Sie HoH verwenden:

my %hash;
$hash{$subref} = { sub => $subref,
                   arg => [qw/bob sue/],
                 };
foreach my $key (keys %hash) {
    print "$key: ref type is: " . ref($key) . "\n";
    $hash{$key}{sub}->( @{ $hash{$key}{arg} } );
}

Aber dann können Sie wahrscheinlich sowieso einen besseren Schlüssel wählen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X