2009-03-11 21 views
12

La pregunta "How can I monkey-patch an instance method in Perl?" me hizo pensar. ¿Puedo redefinir dinámicamente los métodos de Perl? Decir que tengo una clase como ésta:¿Cómo puedo redefinir los métodos de clase Perl?

package MyClass; 
sub new { 
    my $class = shift; 
    my $val = shift; 
    my $self = { val=> $val}; 

    bless($self, $class); 
    return $self; 
}; 

sub get_val { 
    my $self = shift; 
    return $self->{val}+10; 
} 
1; 

Y digamos que la adición de dos números es muy caro.

Me gustaría modificar la clase para que $ val + 10 solo se calcule la primera vez que llamo al método en ese objeto. Las llamadas posteriores al método devolverían un valor en caché.

podría fácilmente modificar el método para incluir el almacenamiento en caché, pero:

  • Tengo un montón de métodos como éste.
  • Prefiero no ensuciar este método.

Lo que realmente quiero hacer es especificar una lista de métodos que sé siempre devuelven el mismo valor para una instancia determinada. Luego quiero tomar esta lista y pasarla a una función para agregar soporte de caché a esos métodos

¿Existe alguna forma efectiva de lograr esto?

Seguimiento. El siguiente código funciona, pero como use strict no permite referencias por cadena, no estoy 100% donde quiero estar.

sub myfn { 
    printf("computing\n"); 
    return 10; 
} 
sub cache_fn { 
    my $fnref = shift; 

    my $orig = $fnref; 
    my $cacheval; 

    return sub { 
    if (defined($cacheval)) { return $cacheval; } 
    $cacheval = &$orig(); 
    return $cacheval; 
    } 
} 

*{myfn} = cache_fn(\&myfn); 

¿Cómo modifico simplemente hacer esto ?:

cache_fn(&myfn); 
+0

{no hay advertencias 'redefinir'; * myfn = cache_fn (\ &myfn);} - Esto elimina las advertencias ambiguas y redefinidas. Consulte mi nueva respuesta sobre cache_fn (&myfn); – draegtun

+0

Es posible que desee examinar las memorias (hay algunos buenos módulos de CPAN) o usar desencadenantes de Moose para caché. cálculos en atributos y recalcularlos según sea necesario con desencadenadores. – Ether

Respuesta

11

puede sobrescribir métodos como get_val de otro paquete como esto:

*{MyClass::get_val} = sub { return $some_cached_value }; 

Si usted tiene una lista de método nombres, podría hacer algo como esto:

my @methods = qw/ foo bar get_val /; 
foreach my $meth (@methods) { 
    my $method_name = 'MyClass::' . $meth; 
    no strict 'refs'; 
    *{$method_name} = sub { return $some_cached_value }; 
} 

¿Es eso lo que imaginas?

+0

use strict; no me permite referirme a las funciones por cadena. ¿Hay alguna forma de evitar esto? – mmccoo

+1

quiere decir una forma que no utiliza refs "no estrictos" "como en mi segundo ejemplo? Ninguna que yo sepa. – innaM

3

Nunca lo he intentado con métodos, pero Memoize puede ser lo que estás buscando. Pero asegúrese de leer el caveats.

+0

Funcionaría con métodos, pero es posible que deba usar la opción NORMALIZER si desea almacenar en caché todas las instancias. Si desea almacenar en caché por instancia, entonces es probable que es más fácil simplemente colocar el valor en el objeto, por ejemplo, devolver $ self -> {foo} si existe $ self -> {foo} – runrig

+0

Eso es más fácil si está escribiendo th e clase. La pregunta implica que está tratando de modificar dinámicamente una clase que no escribió. – cjm

2

No es útil en su caso, pero había sido su clase escrito en Moose continuación, se puede reemplazar de forma dinámica utilizando métodos Class::MOP sus fundamentos ....

{ 
    package MyClass; 
    use Moose; 

    has 'val' => (is => 'rw'); 

    sub get_val { 
     my $self = shift; 
     return $self->val + 10; 
    } 

} 

my $A = MyClass->new(val => 100); 
say 'A: before: ', $A->get_val; 

$A->meta->remove_method('get_val'); 
$A->meta->add_method('get_val', sub { $_[0]->val }); 

say 'A: after: ', $A->get_val; 

my $B = MyClass->new(val => 100); 
say 'B: after: ', $B->get_val; 

# gives u... 
# => A: before: 110 
# => A: after: 100 
# => B: after: 100 
2

¿Cómo modifico simplemente hacer esto ?:

cache_fn (\ & myfn);

Bien basado en su ejemplo actual que podría hacer algo como esto ....

sub cache_fn2 { 
    my $fn_name = shift; 
    no strict 'refs'; 
    no warnings 'redefine'; 

    my $cache_value = &{ $fn_name }; 
    *{ $fn_name } = sub { $cache_value }; 
} 

cache_fn2('myfn'); 

Sin embargo mirando este ejemplo no puedo evitar pensar que se puede utilizar en lugar memoize?

5

Escribo sobre varias cosas diferentes que podría querer hacer en el capítulo "Subrutinas dinámicas" de Mastering Perl. Dependiendo de lo que esté haciendo, puede desear envolver la subrutina o redefinirla, o subclase, o todo tipo de otras cosas.

Perl es un lenguaje dinámico, por lo que hay mucha magia negra que puedes hacer. Usarlo sabiamente es el truco.

+1

gracias por el enlace. Leí un par de páginas en línea y obtuvieron todo lo que necesitaba para responder a esto. Parece que hay otro libro en mi lista de compras. – mmccoo

Cuestiones relacionadas