Как я использую индекс в ссылке на массив как ссылка метода в Perl?

Подобный этому вопросу об итерации по ссылкам подпрограммы, и в результате ответа на этот вопрос о 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 к переменной, затем называя его), вероятно, предпочтителен.

6
задан Community 23 May 2017 в 11:59
поделиться

2 ответа

Как написано, вы можете обойтись без

$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
4
ответ дан 17 December 2019 в 07:01
поделиться

(здесь временный заполнитель до тех пор, пока / пока не вернется исходный плакат с ответом):

Уловка добавляет разыменование:

$instance->${\$sublist->[$index]}(@args);

таким образом вы также можете сделать:

$instance->${\$instance->sublist->[$index]}(@args);

иначе он считает, что это скаляр для разыменования. (например, Не СКАЛЯРНАЯ ссылка на script.pl, строка XX ).

0
ответ дан 17 December 2019 в 07:01
поделиться
Другие вопросы по тегам:

Похожие вопросы: