Как я могу переопределить методы класса Perl? - PullRequest
13 голосов
/ 11 марта 2009

Вопрос «Как я могу исправить патч метода экземпляра в Perl?» заставил меня задуматься. Могу ли я динамически переопределить методы 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;

И скажем, добавление двух чисел действительно дорого.

Я бы хотел изменить класс так, чтобы $ val + 10 вычислялся только при первом вызове метода для этого объекта. Последующие вызовы метода вернут кэшированное значение.

Я мог бы легко изменить метод для включения кэширования, но:

  • У меня есть несколько таких методов.
  • Я бы лучше не испортил этот метод.

Что я действительно хочу сделать, это указать список методов, которые, как я знаю, всегда возвращают одно и то же значение для данного экземпляра. Затем я хочу взять этот список и передать его функции, чтобы добавить поддержку кэширования для этих методов

Есть ли эффективный способ сделать это?

Продолжайте. Приведенный ниже код работает, но поскольку использование strict не разрешает ссылки на строки, я не на 100% хочу быть.

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

Как мне изменить, чтобы просто сделать это?:

cache_fn(&myfn);

Ответы [ 5 ]

15 голосов
/ 11 марта 2009

Вы можете переписать методы, такие как get_val, из другого пакета, например так:

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

Если у вас есть список имен методов, вы можете сделать что-то вроде этого:

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

Это то, что вы представляете?

5 голосов
/ 12 марта 2009

Я пишу о нескольких разных вещах, которые вы, возможно, захотите сделать в главе «Динамические подпрограммы» Освоение Perl . В зависимости от того, что вы делаете, вы можете захотеть обернуть подпрограмму или переопределить ее, или подкласс, или все виды других вещей.

Perl - динамический язык, поэтому вы можете использовать много черной магии. Использование этого с умом - хитрость.

3 голосов
/ 11 марта 2009

Я никогда не пробовал это с методами, но Memoize может быть то, что вы ищете. Но обязательно прочитайте предостережения .

2 голосов
/ 12 марта 2009

Как мне изменить, чтобы просто сделать это?:

cache_fn (\ & myfn);

Ну, основываясь на вашем текущем примере, вы можете сделать что-то вроде этого ...

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

Однако, глядя на этот пример, я не могу не думать, что вместо него можно использовать Memoize?

2 голосов
/ 11 марта 2009

В вашем случае бесполезно, но если ваш класс был написан на Moose , тогда вы можете динамически переопределять методы, используя его Class :: MOP подкрепления ....

{
    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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...