我想将一些OO包中的subs放入一个数组中 - 也在包中 - 用作调度表。像这样的东西
package Blah::Blah;
use fields 'tests';
sub new {
my($class )= @_;
my $self = fields::new($class);
$self->{'tests'} = [
$self->_sub1
,$self->_sub2
];
return $self;
}
_sub1 { ... };
_sub2 { ... };
我不完全确定这个的语法?
$self->{'tests'} = [
$self->_sub1
,$self->_sub2
];
或
$self->{'tests'} = [
\&{$self->_sub1}
,\&{$self->_sub2}
];
或
$self->{'tests'} = [
\&{_sub1}
,\&{_sub2}
];
我似乎无法在OO包中使用它,但它在程序方面非常简单,我没有找到OO的任何示例。
非常感谢任何帮助, 伊恩
答案 0 :(得分:11)
你的朋友是can
。它返回对子例程的引用(如果存在),否则返回null。它甚至可以正确地走向OO链。
$self->{tests} = [
$self->can('_sub1'),
$self->can('_sub2'),
];
# later
for $tn (0..$#{$self->{tests}}) {
ok defined $self->{tests}[$tn], "Function $tn is available.";
}
# and later
my $ref = $self->{tests}[0];
$self->$ref(@args1);
$ref = $self->{tests}[1];
$self->$ref(@args2);
或者,由于this问题(恰好是this问题的变体),您可以直接调用它:
$self->${\$self->{tests}[0]}(@args1);
$self->${\$self->{tests}[1]}(@args1);
请注意,\
为我们提供了对subref的引用,然后在${}
之后$self->
取消引用。呼!
为了解决大脑提到的及时性问题,另一种方法是简单地让{test}成为一个子程序本身,返回一个ref,然后你可以在你需要的时候得到它:
sub tests {
return [
$self->can('_sub1'),
$self->can('_sub2')
];
}
然后使用它:
for $tn (0..$#{$self->tests()}) {
...
}
当然,如果你不得不迭代refs,你也可以直接传递引用:
for my $ref (0..$#{$self->tests()}) {
$self->$ref(@args);
}
答案 1 :(得分:6)
虽然Robert P的答案可能对您有用,但它存在在此过程中尽早修复调度的问题。我倾向于尽可能晚地解决方法,所以我会将tests
数组中的内容保留为方法名称,直到您想要使用它们为止:
$self->{tests} = [
qw( _sub1 _sub2 )
];
动态语言的优势在于,只要您愿意决定将要发生什么,您就可以等待。
当你想要运行它们时,你可以经历与罗伯特已经注意到的相同的过程。我会为它添加一个界面:
foreach my $method_name ( $obj->get_test_methods )
{
$obj->$method_name();
}
这可能会更好,因为没有将测试绑定到现有的方法名称:
foreach my $method_name ( $obj->get_test_methods )
{
$obj->run_test_named( $method_name );
}
run_test_named
可以成为您的调度员,而且可以非常灵活:
sub run_test_named
{
my( $self, $name ) = @_;
# do anything you want, like in Robert's answer
}
您可能想要做的一些事情:
当您将自己决定执行的内容与其实施分开时,您可以获得更多自由。不仅如此,下次调用相同的测试名称时,您可以做一些不同的事情。
答案 2 :(得分:6)
use lib Alpha;
my $foo = Alpha::Foo->new; # indirect object syntax is deprecated
$foo->bar();
my %disp_table = ( bar => sub { $foo->bar() } );
$disp_table{bar}->(); # call it
你需要一个闭包,因为你想把一个方法调用变成一个普通的子程序调用,所以你必须捕获你正在调用该方法的对象。
答案 3 :(得分:3)
有几种方法可以做到这一点。你的第三种方法是最接近的这将存储对数组中两个subs的引用。然后,当你想要调用它们时,你必须确保将它们作为第一个参数传递给它们。
您是否有使用use fields
构造的原因?
如果你想创建自包含的测试子,你可以这样做:
$$self{test} = [
map {
my $code = $self->can($_); # retrieve a reference to the method
sub { # construct a closure that will call it
unshift @_, $self; # while passing $self as the first arg
goto &$code; # goto jumps to the method, to keep 'caller' working
}
} qw/_sub1 _sub2/
];
然后给他们打电话
for (@{ $$self{test} }) {
eval {$_->(args for the test); 1} or die $@;
}