Как я могу элегантно вызвать подпрограмму Perl, имя которой содержится в переменной?

52

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

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

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

# call the sub by the name of $action

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


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

Любые мысли об этом подходе? Подпрограммы "Список белых списков" в таблице диспетчеризации - это то, что я все время буду забывать, и мои клиенты предпочли бы, чтобы я ошибался на стороне "он работает", чем "он нечестивый". (очень ограниченное время для разработки приложений)


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

  • 3
    Поймите, что когда вы берете ссылку на что-то, чтобы немедленно разыменовать его, вы можете пропустить средние шаги. :)
  • 1
    @brian: обычно (и определенно в этом случае), но см. stackoverflow.com/questions/1836178/… контрпример. :)
Показать ещё 1 комментарий
Теги:

12 ответов

75
Лучший ответ

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

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

Затем вы можете легко позвонить справа:

$actions{$action}->();

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

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

  • 1
    Я повторил свой комментарий здесь из другого ответа ниже, так как этот ответ на сегодняшний день имеет самый высокий балл! в случае, если это кому-нибудь поможет, если вы также хотите передать параметры подпрограмме, вы просто помещаете их между () - т.е. так же, как и в обычную подпрограмму. например, $actions{$action}->($parmVariable,11,'a'); Параметры должны быть согласованы во всех возможных вызываемых подпрограммах, хотя значения, конечно, могут быть получены из переменных.
20

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

my $disp = {foo => \&some_sub, bar => \&some_other_sub };
$disp->{'foo'}->();
  • 0
    Используя решение & $ action, я получаю: не могу использовать строку ("main :: instructors") в качестве ссылки на подпрограмму, а "строгие ссылки". Так занимаюсь ли я гимнастикой только потому, что у меня строгий режим?
  • 6
    Да, этот вид использования - это то, что запрещает «строгое использование» (среди прочего). Используйте «нет строгих« ссылок »», чтобы разрешить это, но опять же, реальные ссылки или диспетчеры обычно намного понятнее.
Показать ещё 3 комментария
16

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

    $action->()

Пример:

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


    $ perl subfromscalar.pl
    11

Конструкции вроде

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

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

  • 10
    Если вы use strict (и, конечно, вы делаете), вам нужно будет no strict 'refs' use strict qw(vars subs) no strict 'refs' или просто use strict qw(vars subs) чтобы использовать этот синтаксис.
11

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

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

# 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

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

Как правило, наилучшим способом обработки таких случаев является использование таблиц отправки:

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

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

$dispatch{ $your_variable }->( 'any', 'args' );
6

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

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

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

  • 1
    и в случае, если это кому-нибудь поможет, если вы также хотите передать параметры подпрограмме, вы просто помещаете их между () - то есть так же, как и в обычную подпрограмму. например, $sub->($parmVariable,11,'a');
  • 0
    Чтобы вызвать это рефлексивно изнутри sub; my $ this = \ & {(caller (0)) [3]}; $ return = $ this -> (); $ return. = $ this -> (\ @ flags, $ arguments, \% foo);
5
__PACKAGE__->can($action)->(@args);

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

3

Каждый пакет в 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.

  • 0
    Это длинная страница для ссылки, но я думаю, что вы имеете в виду раздел Таблицы символов .
1

Я сделал это следующим образом:

@func = qw(cpu mem net disk);
foreach my $item (@func){
    $ret .= &$item(1);
}
1

Используйте

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

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

eval("$action()");
0

Я использовал это: он работает для меня.

(\$action)->();

Или вы можете использовать 'do', очень похожее на предыдущие сообщения:

$p = do { \&$conn;}; 
$p->();
0

Если это только в одной программе, напишите функцию, которая вызывает подпрограмму, используя имя переменной, и только нужно документировать ее/извиниться один раз?

Ещё вопросы

Сообщество Overcoder
Наверх
Меню