2009-03-11 20 views
12

Pytanie "How can I monkey-patch an instance method in Perl?" zmusiło mnie do myślenia. Czy mogę dynamicznie przedefiniować metody Perla? Załóżmy, że mam taką klasę:Jak mogę przedefiniować metody klasy Perla?

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; 

Załóżmy, że dodanie dwóch liczb jest naprawdę drogie.

Chciałbym zmodyfikować klasę tak, aby $ val + 10 było obliczane tylko przy pierwszym wywołaniu metody na tym obiekcie Kolejne wywołania metody zwrócą buforowaną wartość.

mogę łatwo zmodyfikować sposób obejmuje buforowanie, ale:

  • mam kilka sposobów, takich jak to.
  • Wolę nie brudzić tej metody.

Co naprawdę chcę zrobić, to podać listę metod, które znam zawsze zwracają tę samą wartość dla danej instancji. Następnie chcę wziąć tę listę i przekazać ją do funkcji dodawania buforowania do tych metod.

Czy istnieje skuteczny sposób, aby to osiągnąć?

Kontynuacja. Poniższy kod działa, ale ponieważ użycie strict nie zezwala na odwołania za pomocą ciągu znaków, nie jestem w 100% tam, gdzie chcę być.

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); 

Jak zmodyfikować po prostu to zrobić ?:

cache_fn(&myfn); 
+0

{brak ostrzeżeń "przedefiniowanie"; * myfn = cache_fn (\ &myfn);} - To pozbywa się niejednoznacznych i redefiniowanych ostrzeżeń.Zobacz moją nową odpowiedź na temat cache_fn (&myfn); – draegtun

+0

Możesz zajrzeć do notowania (jest kilka dobrych modułów CPAN) lub użyć wyzwalaczy Moose do buforowania drogich obliczenia w atrybutach i przeliczyć je w razie potrzeby za pomocą wyzwalaczy – Ether

Odpowiedz

11

Można nadpisać metod takich jak get_val z innego pakietu jak ten:

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

Jeśli masz listę metody nazwy, możesz zrobić coś takiego:

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 }; 
} 

Czy to jest to, co sobie wyobrażasz?

+0

używać strict, nie pozwala mi odnieść się do funkcji przez ciąg.Jest to sposób na obejście tego bezpiecznie – mmccoo

+1

masz na myśli sposób, który nie używa "nie surowe" refs "" jak w moim drugim przykładzie? Żadnych, które znam z. – innaM

3

Nigdy nie próbowałem go metodami, ale Memoize może być tym, czego szukasz. Ale pamiętaj, aby przeczytać caveats.

+0

To działałoby z metodami, ale możesz potrzebować użyć opcji NORMALIZER, jeśli chcesz cache we wszystkich instancjach. Jeśli chcesz cache na instancję, to prawdopodobnie łatwiej jest po prostu trzymać wartość w obiekcie, np. return $ self -> {foo} jeśli istnieje $ self -> {foo} – runrig

+0

Łatwiej, jeśli piszesz e klasa. Pytanie sugeruje, że próbuje on dynamicznie modyfikować klasę, której nie napisał. – cjm

2

Nie przydatna w przypadku, ale nie klasa została napisana w Moose następnie można dynamicznie przesłonić metody korzystania z jego Class::MOP podstaw ....

{ 
    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

Jak zmodyfikować po prostu to zrobić ?:

cache_fn (\ & myfn);

Dobrze podstawie bieżącej przykład można zrobić coś takiego ....

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'); 

Jednak patrząc na ten przykład nie mogę oprzeć się myśli, że można użyć Memoize zamiast?

5

Piszę o kilku różnych rzeczach, które możesz chcieć wykonać w rozdziale "Dynamiczne podprogramy" pod numerem Mastering Perl. W zależności od tego, co robisz, możesz ominąć podprocedurę lub przedefiniować ją, podklasę lub inne rzeczy.

Perl to dynamiczny język, więc można zrobić wiele czarnych magii. Używanie jej mądrze jest lewą.

+1

dzięki za link. Przeczytałem kilka stron internetowych i mają to, czego bym potrzebował, aby odpowiedzieć na to pytanie. Wygląda na to, że inna książka znajduje się na mojej liście zakupów. – mmccoo

Powiązane problemy