2009-12-02 3 views
8

Ich bin sicher, dass dies in der Dokumentation irgendwo abgedeckt ist, aber ich konnte es nicht finden ... Ich bin auf der Suche nach dem syntaktischen Zucker, der es möglich macht, eine Methode für eine Klasse aufzurufen, deren Name in einem gespeichert ist Hash (im Gegensatz zu einer einfachen skalaren gegen):Wie rufe ich einen Funktionsnamen auf, der in Perl in einem Hash gespeichert ist?

use strict; use warnings; 

package Foo; 
sub foo { print "in foo()\n" } 

package main; 
my %hash = (func => 'foo'); 

Foo->$hash{func}; 

Wenn ich $hash{func} in eine skalare Variable zuerst kopieren, dann kann ich Foo->$func ganz gut nennen ... aber was Foo->$hash{func} zu ermöglichen fehlt zu arbeiten?

(EDIT: Ich möchte nicht etwas Besonderes tun, indem eine Methode auf Klasse Foo aufrufen - das könnte genauso gut ein gesegnetes Objekt sein (und in meinem eigentlichen Code ist es), es war einfach einfacher zu schreiben ein eigenständiges Beispiel mit einer Klassenmethode.)

EDIT 2: Nur zur Vollständigkeit der Kommentare unten, das ist, was ich eigentlich tue (dies ist in einer Bibliothek von Moose Attribut Zucker, erstellt mit Moose::Exporter) :

# adds an accessor to a sibling module 
sub foreignTable 
{ 
    my ($meta, $table, %args) = @_; 

    my $class = 'MyApp::Dir1::Dir2::' . $table; 
    my $dbAccessor = lcfirst $table; 

    eval "require $class" or do { die "Can't load $class: [email protected]" }; 

    $meta->add_attribute(
     $table, 
     is => 'ro', 
     isa => $class, 
     init_arg => undef, # don't allow in constructor 
     lazy => 1, 
     predicate => 'has_' . $table, 
     default => sub { 
      my $this = shift; 
      $this->debug("in builder for $class"); 

      ### here's the line that uses a hash value as the method name 
      my @args = ($args{primaryKey} => $this->${\$args{primaryKey}}); 
      push @args, (_dbObject => $this->_dbObject->$dbAccessor) 
       if $args{fkRelationshipExists}; 

      $this->debug("passing these values to $class -> new: @args"); 
      $class->new(@args); 
     }, 
    ); 
} 

ich habe die markierte Zeile oben mit dieser Fassung:

 my $pk_accessor = $this->meta->find_attribute_by_name($args{primaryKey})->get_read_method_ref; 
     my @args = ($args{primaryKey} => $this->$pk_accessor); 

PS. Ich habe gerade bemerkt, dass die gleiche Technik (mit der Moose Meta-Klasse, um die coderef nachschlagen, anstatt seine Namenskonvention vorausgesetzt) ​​kann nicht auch für Prädikate verwendet werden, wie Class::MOP::Attribute hat keine ähnliche get_predicate_method_ref Accessor. :(

+0

Ich glaube nicht, dass möglich ist, aufgrund der Perl-Parsing-Reihenfolge. Warum willst du $ hash {func} nicht zuerst in einen Skalar kopieren? –

+0

Kein besonderer Grund, außer es scheint unnötig, und dies war ein interessantes Puzzle, das mich ratlos. Ich glaubte das nicht einfach, weil ich die Antwort nicht wusste, dass es keine Antwort gab. :) (tl; dr-version: weil ich neugierig bin!) – Ether

+0

Äh, mir scheint, wenn du Elch benutzt, dann gehst du immer noch in die falsche Richtung. Eine der Eigenschaften von Moose ist das Meta-Objektmodell, auf dem es aufgebaut ist ... Ich habe das Gefühl, dass es eine Methode gibt, die man aufrufen kann, um den tatsächlichen Namen nach Zeichenfolgen zu suchen, den man dann aufrufen könnte, statt blanke Zeichenfolgen zu verwenden . Ich weiß es nicht von ganz oben, obwohl ... –

Antwort

14
Foo->${\$hash{func}}; 

Aber für Klarheit verwenden können, würde ich wahrscheinlich noch schreiben wie:

my $method = $hash{func}; 
Foo->$method; 
+0

Einfach * und * offensichtlich; Ich liebe es! – Ether

+0

Ich verstehe das erste Dollarzeichen. Aber was ich nicht verstehe, ist der Backslash. – innaM

+1

@Manni: Die $ {...} wird eher als Skalar-Dereferenz interpretiert als als Disambiguierung, da der Inhalt keine Literal-Zeichenfolge ist. Der umgekehrte Schrägstrich soll einen Verweis auf den Wert von $ hash {func} erstellen. –

2

Gibt es einen Grund, warum Sie Subroutinennamen Speicherung anstelle der Referenzen zu codieren?

zB

use strict; use warnings; 

package Foo; 
sub foo { print "in foo()\n" } 

package main; 
my %hash = (func => \&Foo::foo); 

$hash{func}->(); 

Sie werden nicht den Klassennamen werden vorbei, aber wenn das für Sie wichtig , Sie so etwas wie

my %hash = (func => sub { return Foo->foo(@_) }); 
+0

Ja, weil die Subnamen den Attributnamen entsprechen, die aus Moose-Objekten stammen. – Ether

+0

Ich habe meine Frage bearbeitet, um anzuzeigen, dass "Foo" entweder ein Klassenname oder ein gesegnetes Objekt sein kann. – Ether

+0

Sie möchten keine Subreferenz, da dies die Vererbung unterbricht. –

1

Haben Sie versucht UNIVERSAL'skönnen Methode? Sie sollten so etwas wie dies umsetzen können:

## untested 
if (my $code = $object->can($hash{func})) { 
    $object->$code(); 
} 

Ich habe ein nutzloses, einzeiliges Beispiel zu demonstrieren:

perl -MData::Dumper -le 'my %h = (f => "Dump"); my $o = Data::Dumper->new([qw/1 2 3/]); my $ref = $o->can($h{f}); print $o->$ref()' 
+0

Ja, aber auch dies ist eine Ablenkung von meiner ursprünglichen Frage, wie man einen Hash-Wert als Methodenname verwendet (welcher runrig beantwortet hat). 'can' ist in diesem Fall eine weniger perfekte Lösung als die, die ich in einem Frage-Edit skizziert habe, da es Annahmen über die Benennung eines Moose-Lesers macht. – Ether

Verwandte Themen