我保留了我想在运行时在名为$ action的变量中调用的子例程的名称。然后我用它来在正确的时间调用该子:
&{\&{$action}}();
工作正常。我唯一不喜欢的是它很难看,每次我这样做,我都感到很高兴为下一个开发者添加评论:
# call the sub by the name of $action
任何人都知道更漂亮的方式吗?
更新:这里的想法是避免每次添加新的可调用子时都必须维护一个调度表,因为我是唯一的开发人员,我不担心其他程序员遵循或不遵循“规则”。为了方便我牺牲了一点安全性。相反,我的调度模块将检查$ action以确保1)它是已定义的子例程的名称而不是与eval一起运行的恶意代码,以及2)它不会运行任何以下划线开头的子,这将是这个命名约定标记为仅限内部的子。
对这种方法有何想法?发送表中的白名单子程序是我一直都会忘记的事情,我的客户宁愿我在“它工作”而不是“它是邪恶的安全”方面犯错误。 (开发应用程序的时间非常有限)
最终更新:我认为毕竟我决定在调度表上。虽然我很好奇,如果读过这个问题的人曾经试图废除一个以及他们是如何做到的,那么我不得不向这里的集体智慧屈服。感谢所有,很多很好的回应。
答案 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->();