我怎样才能优雅地调用名称保存在变量中的Perl子例程?

时间:2009-12-16 16:00:50

标签: perl

我保留了我想在运行时在名为$ action的变量中调用的子例程的名称。然后我用它来在正确的时间调用该子:

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

工作正常。我唯一不喜欢的是它很难看,每次我这样做,我都感到很高兴为下一个开发者添加评论:

# call the sub by the name of $action

任何人都知道更漂亮的方式吗?


更新:这里的想法是避免每次添加新的可调用子时都必须维护一个调度表,因为我是唯一的开发人员,我不担心其他程序员遵循或不遵循“规则”。为了方便我牺牲了一点安全性。相反,我的调度模块将检查$ action以确保1)它是已定义的子例程的名称而不是与eval一起运行的恶意代码,以及2)它不会运行任何以下划线开头的子,这将是这个命名约定标记为仅限内部的子。

对这种方法有何想法?发送表中的白名单子程序是我一直都会忘记的事情,我的客户宁愿我在“它工作”而不是“它是邪恶的安全”方面犯错误。 (开发应用程序的时间非常有限)


最终更新:我认为毕竟我决定在调度表上。虽然我很好奇,如果读过这个问题的人曾经试图废除一个以及他们是如何做到的,那么我不得不向这里的集体智慧屈服。感谢所有,很多很好的回应。

12 个答案:

答案 0 :(得分:79)

不是将子例程名称存储在变量中并调用它们,更好的方法是使用子例程引用的哈希值(也称为dispatch table。)

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

然后你可以轻松地拨打正确的电话:

$actions{$action}->();

您还可以添加一些检查以确保$action是哈希中的有效密钥,依此类推。

一般来说,你应该避免使用符号引用(你现在正在做什么),因为它们会导致各种各样的问题。此外,使用实际子例程引用将与strict打开一起使用。

答案 1 :(得分:21)

只是&$action(),但通常从一开始就使用coderefs更好,或使用调度程序哈希。例如:

my $disp = {foo => \&some_sub, bar => \&some_other_sub };
$disp->{'foo'}->();

答案 2 :(得分:16)

咦?你可以说

    $action->()

示例:

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


    $ perl subfromscalar.pl
    11

这样的结构
    'f'->()     # equivalent to   &f()

也有效。

答案 3 :(得分: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"
}

答案 4 :(得分:10)

最重要的是:为什么要将变量用作函数名。如果它是'eval'会发生什么? 是否有可以使用的功能列表?或者它可以是任何功能?如果列表存在 - 它有多长时间?

通常,处理此类情况的最佳方法是使用调度表:

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

然后只是:

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

答案 5 :(得分:6)

我做了类似的事情。我把它分成两行,使它更容易识别,但它并不是更漂亮。

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

我不知道更正确或更漂亮的方式。对于它的价值,我们拥有能够完成您正在做的事情的生产代码,并且无需禁用use strict即可运行。

答案 6 :(得分:6)

__PACKAGE__->can($action)->(@args);

有关can()的更多信息:http://perldoc.perl.org/UNIVERSAL.html

答案 7 :(得分: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

答案 8 :(得分:1)

使用

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

或使用eval执行该功能:

eval("$action()");

答案 9 :(得分:1)

我是这样做的:

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

答案 10 :(得分:0)

如果只在一个程序中,编写一个使用变量名称调用子程序的函数,只需记录一次/道歉一次?

答案 11 :(得分:0)

我用过这个:它对我有用。

(\$action)->();

或者您可以使用&#39; do&#39;,与以前的帖子非常相似:

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