2009-12-02 7 views
8

Estoy seguro de que esto está cubierto en la documentación en alguna parte, pero no he podido encontrarlo ... Estoy buscando el azúcar sintáctico que permitirá llamar a un método en una clase cuyo nombre está almacenado en una almohadilla (en contraposición a un simple escalar):¿Cómo llamo a un nombre de función que está almacenado en un hash en Perl?

use strict; use warnings; 

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

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

Foo->$hash{func}; 

Si copio $hash{func} en una variable escalar primero, entonces yo puedo llamar Foo->$func muy bien ... pero lo que falta para que Foo->$hash{func} a trabajar?

(EDITAR: No me refiero a hacer nada especial al llamar a un método en la clase Foo - esto podría ser un objeto bendito (y en mi código real), simplemente fue más fácil escribir un ejemplo autónomo utilizando un método de clase.)

EDIT 2: Para completar los comentarios a continuación, esto es lo que estoy haciendo en realidad (esto es en una biblioteca de azúcar atributo Moose, creado con 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); 
     }, 
    ); 
} 

he reemplazado por encima de la línea marcada con esto:

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

PS. Me acabo de dar cuenta de que esta misma técnica (utilizando la clase meta de Moose para buscar el cifrado en lugar de asumir su convención de nomenclatura) no se puede usar para los predicados, ya que Class::MOP::Attribute no tiene un acceso similar get_predicate_method_ref. :(

+0

No creo que esto es posible debido a la orden de análisis de Perl. ¿Por qué no quieres copiar $ hash {func} en un escalar primero? –

+0

No hay una razón en particular, excepto que parece innecesario, y este fue un rompecabezas interesante que me dejó perplejo. No lo creí simplemente porque no sabía la respuesta de que no había respuesta. :) (tl; versión dr: ¡porque tengo curiosidad!) – Ether

+0

Er, me parece que si estás usando Moose, entonces sigues haciéndolo de la manera incorrecta. Una de las características de Moose es el modelo de metaobjeto sobre el que está basado ... Tengo la sensación de que hay un método que puede invocar para buscar el sub actual por nombre de cadena, que luego podría llamar, en lugar de utilizar cadenas simples . Aunque no lo sé por lo alto de la cabeza ... –

Respuesta

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

Pero para mayor claridad, probablemente todavía escribo como:

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

Simple * y * obvio; ¡Lo amo! – Ether

+0

Entiendo el primer signo de dólar. Pero lo que no entiendo es la barra invertida. – innaM

+1

@Manni: El $ {...} se analiza como una desreferencia escalar en lugar de una desambiguación porque el contenido no es una cadena literal. La barra diagonal inversa es para crear una referencia al valor de $ hash {func}. –

2

¿Existe una razón por la que está almacenando nombres de subrutina en lugar de las referencias a codificar?

por ejemplo

use strict; use warnings; 

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

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

$hash{func}->(); 

No se le pasa el nombre de la clase, pero si eso es importante para usted , se puede usar algo como

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

Sí, porque los nombres secundarios corresponden a nombres de atributos tomados de objetos Moose. – Ether

+0

He editado mi pregunta para indicar que 'Foo' podría ser un nombre de clase o un objeto bendecido. – Ether

+0

No desea una referencia secundaria porque eso rompe la herencia. –

1

¿Ha intentado UNIVERSAL'spuede método? Usted debe ser capaz de implementar algo como esto:

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

hice un inútil, ejemplo de una línea de demostrar:

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

Sí, pero una vez más esto es una desviación de mi pregunta original sobre cómo usar un valor hash como nombre de método (que runrig respondió). 'can' en este caso es una solución menos perfecta que la que describí en una edición de pregunta, ya que hace suposiciones sobre el nombre de un lector de Moose. – Ether

Cuestiones relacionadas