Как я могу элегантно вызвать подпрограмму Perl, имя которой содержится в переменной? - PullRequest
54 голосов
/ 16 декабря 2009

Я храню имя подпрограммы, которую я хочу вызвать во время выполнения, в переменной с именем $ action. Затем я использую это для вызова этого саба в нужное время:

&{\&{$action}}();

Работает нормально. Единственное, что мне не нравится, это то, что это ужасно, и каждый раз, когда я делаю это, я чувствую себя обязанным добавить комментарий для следующего разработчика:

# call the sub by the name of $action

Кто-нибудь знает более красивый способ сделать это?


ОБНОВЛЕНИЕ: Идея здесь состояла в том, чтобы избежать необходимости поддерживать таблицу диспетчеризации каждый раз, когда я добавляю новую вызываемую подпрограмму, поскольку я являюсь единственным разработчиком, я не беспокоюсь о том, что другие программисты будут следовать или не следовать «правилам». Пожертвовал немного безопасности для моего удобства. Вместо этого мой диспетчерский модуль проверял бы $ action, чтобы убедиться, что 1) это имя определенной подпрограммы, а не вредоносный код для запуска с eval, и 2) что он не будет запускать подпрограммы с предваряющим подчеркиванием, что будет в соответствии с этим соглашением об именах помечается как внутренняя подпрограмма.

Есть какие-нибудь мысли об этом подходе? Белые подпрограммы в таблице диспетчеризации - это то, что я буду постоянно забывать, и мои клиенты скорее будут ошибаться на стороне «это работает», чем «это злобно безопасно». (очень ограниченное время для разработки приложений)


ЗАКЛЮЧИТЕЛЬНОЕ ОБНОВЛЕНИЕ: Я думаю, что я все-таки определился с таблицей отправки. Хотя мне было бы любопытно, если бы кто-нибудь, кто читает этот вопрос, когда-либо пытался покончить с одним и тем, как они это сделали, я должен здесь склониться перед коллективной мудростью. Спасибо всем, много замечательных ответов.

Ответы [ 12 ]

79 голосов
/ 16 декабря 2009

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

my %actions = ( foo => \&foo,
                bar => \&bar,
                baz => sub { print 'baz!' } 
                ... 
              );

Тогда вы можете легко позвонить нужному:

$actions{$action}->();

Вы также можете добавить некоторые проверки, чтобы убедиться, что $action является допустимым ключом в хэше и т. Д.

В общем, вам следует избегать символьных ссылок (что вы делаете сейчас), поскольку они вызывают всевозможные проблемы. Кроме того, использование реальных ссылок на подпрограммы будет работать при включенном strict.

21 голосов
/ 16 декабря 2009

Просто &$action(), но обычно лучше использовать coderefs с самого начала или использовать хэш-диспетчер. Например:

my $disp = {foo => \&some_sub, bar => \&some_other_sub };
$disp->{'foo'}->();
16 голосов
/ 16 декабря 2009

А? Вы можете просто сказать

    $action->()

Пример:

    sub f { return 11 }
    $action = 'f';
    print $action->();


    $ perl subfromscalar.pl
    11

Конструкции типа

    'f'->()     # equivalent to   &f()

также работают.

11 голосов
/ 16 декабря 2009

Я не уверен, что понимаю, что вы имеете в виду. (Я думаю, что это еще один вопрос из недавней группы вопросов «Как использовать переменную в качестве имени переменной?», Но, возможно, нет.)

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

# create the $action variable - a reference to the subroutine
my $action = \&preach_it;
# later - perhaps much later - I call it
$action->();

sub preach_it {
    print "Can I get an amen!\n"
}
10 голосов
/ 16 декабря 2009

Самое важное: почему вы хотите использовать переменную в качестве имени функции. Что будет, если это будет «eval»? Есть ли список функций, которые можно использовать? Или это может быть какая-то функция? Если список существует - как долго это будет?

Как правило, лучший способ обработки таких случаев - это использовать таблицы отправки:

my %dispatch = (
   'addition' => \&some_addition_function,
   'multiplication' => sub { $self->call_method( @_ ) },
);

А потом просто:

$dispatch{ $your_variable }->( 'any', 'args' );
6 голосов
/ 12 марта 2016
__PACKAGE__->can($action)->(@args);

Для получения дополнительной информации о банке (): http://perldoc.perl.org/UNIVERSAL.html

6 голосов
/ 16 декабря 2009

Я делаю что-то подобное. Я разделил его на две строки, чтобы сделать его немного более узнаваемым, но он не намного красивее.

my $sub = \&{$action};
$sub->();

Я не знаю более правильного или более красивого способа сделать это. Что бы это ни стоило, у нас есть производственный код, который делает то, что вы делаете, и он работает без отключения use strict.

3 голосов
/ 05 января 2016

Каждый пакет в Perl уже является хеш-таблицей. Вы можете добавлять элементы и ссылаться на них с помощью обычных операций хеширования. В общем случае нет необходимости дублировать функциональность с помощью дополнительной хэш-таблицы.

#! /usr/bin/perl -T
use strict;
use warnings;

my $tag = 'HTML';

*::->{$tag} = sub { print '<html>', @_, '</html>', "\n" };

HTML("body1");

*::->{$tag}("body2");

Код распечатывается:

<html>body1</html>
<html>body2</html>

Если вам нужно отдельное пространство имен, вы можете определить выделенный пакет.

См. perlmod для получения дополнительной информации.

1 голос
/ 17 марта 2014

Я сделал это так:

@func = qw(cpu mem net disk);
foreach my $item (@func){
    $ret .= &$item(1);
}
1 голос
/ 30 октября 2013

Либо использовать

&{\&{$action}}();

Или используйте eval для выполнения функции:

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