Как использовать индекс в ссылке на массив в качестве ссылки на метод в Perl? - PullRequest
6 голосов
/ 10 мая 2010

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

но как бы мы это сделали, не удалив сначала ссылку?

Edit:

Изменен пример кода, чтобы люди не зацикливались на деталях реализации (вздох, старался изо всех сил). Важное различие между этой и первой ссылкой, которую я дал, заключалось в том, что функция должна вызываться как метод , а не как прямая подпрограмма.

Редактировать 2:

См. Обсуждение в связанном комментарии о технических деталях и о том, почему более предпочтительным является более длинный путь (сохранение subref в переменной, затем ее вызов).

Ответы [ 2 ]

4 голосов
/ 10 мая 2010

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

$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
0 голосов
/ 10 мая 2010

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

Хитрость заключается в добавлении разыменования:

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

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

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

в противном случае он считает, что это скаляр к разыменованию. (например, Not a SCALAR reference at script.pl, line XX).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...