Как я могу переопределить методы класса Perl?

Вопрос "Как я могу обезвредить метод экземпляра в 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);

Ответы

Ответ 1

Вы можете перезаписать такие методы, как 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 };
}

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

Ответ 2

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

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

Ответ 3

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

Ответ 4

Не полезно в вашем случае, но если ваш класс был написан в Moose, тогда вы можете динамически переопределять методы, используя Класс:: 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

Ответ 5

Как мне изменить только для этого?:

     

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?