Подобный этому вопросу об итерации по ссылкам подпрограммы, и в результате ответа на этот вопрос о OO диспетчеризируют таблицу, я задавался вопросом, как назвать ссылку метода в ссылке, не удаляя его сначала, или если это было даже возможно.
Например:
package Class::Foo;
use 5.012; #Yay autostrict!
use warnings;
# a basic constructor for illustration purposes....
sub new {
my $class = shift;
return bless {@_}, $class;
}
# some subroutines for flavor...
sub sub1 { say 'in sub 1'; return shift->{a} }
sub sub2 { say 'in sub 2'; return shift->{b} }
sub sub3 { say 'in sub 3'; return shift->{c} }
# and a way to dynamically load the tests we're running...
sub sublist {
my $self = shift;
return [
$self->can('sub1'),
$self->can('sub3'),
$self->can('sub2'),
];
}
package main;
sub get_index { ... } # details of how we get the index not important
my $instance = Class::Foo->new(a => 1, b => 2, c => 3);
my $subs = $instance->sublist();
my $index = get_index();
# <-- HERE
Так, в ЗДЕСЬ, мы могли сделать:
my $ref = $subs->[$index];
$instance->$ref();
но как мы сделали бы это, не удаляя ссылку сначала?
Править:
Измененный пример кода так люди не становится одержимым деталями реализации (вздох, старался изо всех сил). Важное различие между этим и первой ссылкой, которую я дал, было то, что функция должна быть вызвана как метод, не как прямая подпрограмма.
Редактирование 2:
Посмотрите обсуждение в связанном комментарии о технических деталях, и почему более длинный путь (хранящий subref к переменной, затем называя его), вероятно, предпочтителен.
Как написано, вы можете обойтись без
$tests->[$index]();
потому что методы в вашем вопросе не используют $self
.
Вы могли бы передавать $instance
в явном виде, но это неудобно. Лучше имитировать делегаты с помощью замыканий:
sub sublist {
my $self = shift;
my $sublist;
for (qw/ sub1 sub3 sub2 /) {
my $meth = $_;
push @$sublist => sub { $self->$meth() };
}
return $sublist;
}
Если вы предпочитаете быть лаконичным, используйте
sub sublist {
my $self = shift;
return [ map { my $meth = $_; sub { $self->$meth() } }
qw/ sub1 sub3 sub2 / ];
}
Вызов одного из них наугад все равно
$tests->[$index]();
но теперь методы получают вызывающих.
Захват подрефов через can
кажется ненужной сложностью. Если будет достаточно определяемого временем выполнения списка имен методов для вызова, то можно значительно упростить код:
sub sublist {
my $self = shift;
return [ qw/ sub1 sub3 sub2 / ];
}
Ниже мы вызываем их все в целях тестирования, но также можно посмотреть, как вызвать только один:
foreach my $method (@$subs) {
my $x = $instance->$method();
say "$method returned $x";
}
Выход:
in sub 1 sub1 returned 1 in sub 3 sub3 returned 3 in sub 2 sub2 returned 2
(здесь временный заполнитель до тех пор, пока / пока не вернется исходный плакат с ответом):
Уловка добавляет разыменование:
$instance->${\$sublist->[$index]}(@args);
таким образом вы также можете сделать:
$instance->${\$instance->sublist->[$index]}(@args);
иначе он считает, что это скаляр для разыменования. (например, Не СКАЛЯРНАЯ ссылка на script.pl, строка XX
).