问题"How can I monkey-patch an instance method in 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);
答案 0 :(得分:14)
您可以从另一个包中覆盖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 };
}
这就是你想象的吗?
答案 1 :(得分:5)
我在Mastering Perl的“动态子程序”一章中写了几篇你可能想要做的事情。根据您正在做的事情,您可能希望包装子例程,或重新定义它,子类或其他各种事物。
Perl是一种动态语言,所以你可以做很多黑魔法。明智地使用它是诀窍。
答案 2 :(得分:3)
答案 3 :(得分:2)
在你的情况下没有用,但你的课程是用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
答案 4 :(得分:2)
如何修改才能执行此操作?:
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吗?