Как я могу переопределить методы класса 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 вычислялось только при первом вызове метода для этого объекта. Последующие вызовы метода вернут кешированное значение.

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

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

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

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

Следовать за. Приведенный ниже код работает, но поскольку use 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);

person mmccoo    schedule 11.03.2009    source источник
comment
{нет предупреждений "переопределить"; * myfn = cache_fn (\ & myfn); } - Это избавляет от неоднозначных предупреждений и переопределяет их. См. Мой новый ответ о cache_fn (& myfn);   -  person draegtun    schedule 12.03.2009
comment
Возможно, вы захотите изучить мемоизацию (есть несколько хороших модулей CPAN) или использовать триггеры Moose для кэширования дорогостоящих вычислений в атрибутах и ​​их пересчета по мере необходимости с помощью триггеров.   -  person Ether    schedule 09.12.2009


Ответы (5)


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

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

person innaM    schedule 11.03.2009
comment
используйте строгий; не позволяет мне ссылаться на функции по строкам. Есть ли способ безопасно обойти это? - person mmccoo; 11.03.2009
comment
Вы имеете в виду способ, в котором не используются строгие ссылки, как в моем втором примере? Ничего из того, что я знаю. - person innaM; 12.03.2009

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

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

person brian d foy    schedule 12.03.2009
comment
спасибо за ссылку. Я прочитал пару страниц в Интернете, и у них есть то, что мне нужно, чтобы ответить на этот вопрос. Похоже, в моем списке покупок есть еще одна книга. - person mmccoo; 12.03.2009

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

person cjm    schedule 11.03.2009
comment
Он будет работать с методами, но вам, возможно, придется использовать параметр NORMALIZER, если вы хотите кэшировать для всех экземпляров. Если вы хотите кэшировать для каждого экземпляра, то, вероятно, проще просто вставить значение в объект, например вернуть $ self - ›{foo}, если существует $ self -› {foo} - person runrig; 11.03.2009
comment
Так проще, если вы пишете класс. Вопрос подразумевает, что он пытается динамически изменить класс, который он не писал. - person cjm; 11.03.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
person draegtun    schedule 11.03.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?

person draegtun    schedule 12.03.2009