[英]How can I elegantly call a Perl subroutine whose name is held in a variable?
我保留了我想在运行时在名为$ action的变量中调用的子例程的名称。 然后我用它在正确的时间调用该子:
&{\&{$action}}();
工作良好。 我唯一不喜欢的是它很丑陋,每次我这样做,我都感到很高兴为下一个开发者添加评论:
# call the sub by the name of $action
有人知道更漂亮的方式吗?
更新:这里的想法是避免每次添加新的可调用子时都必须维护一个调度表,因为我是唯一的开发人员,我不担心其他程序员遵循或不遵循“规则”。 为了方便我牺牲了一点安全性。 相反,我的调度模块将检查$ action以确保1)它是已定义的子例程的名称而不是与eval一起运行的恶意代码,以及2)它不会运行任何以下划线开头的子,这将是通过此命名约定标记为仅限内部的subs。
有关这种方法的任何想法? 发送表中的白名单子程序是我一直都会忘记的东西,我的客户宁愿我在“它工作”而不是“它是邪恶的安全”方面犯错误。 (开发应用程序的时间非常有限)
最后更新:我想我毕竟已经决定了一个调度表。 虽然我很好奇,如果读过这个问题的人曾经试图废除一个以及他们是如何做到的,那么我不得不向这里的集体智慧屈服。 感谢所有,许多伟大的回应。
不是将子例程名称存储在变量中并调用它们,更好的方法是使用子例程引用的哈希(也称为分派表) 。
my %actions = ( foo => \&foo,
bar => \&bar,
baz => sub { print 'baz!' }
...
);
然后你可以轻松地拨打正确的电话:
$actions{$action}->();
您还可以添加一些检查以确保$action
是哈希中的有效键,依此类推。
一般来说,你应该避免使用符号引用(你现在正在做什么),因为它们会导致各种各样的问题。 此外,使用实际子例程引用将strict
打开。
只是&$action()
,但通常从一开始就使用coderefs更好,或使用调度程序哈希。 例如:
my $disp = {foo => \&some_sub, bar => \&some_other_sub };
$disp->{'foo'}->();
咦? 你可以说
$action->()
例:
sub f { return 11 }
$action = 'f';
print $action->();
$ perl subfromscalar.pl
11
像。这样的结构
'f'->() # equivalent to &f()
也工作。
我不确定我明白你的意思。 (我认为这是最近一组“如何使用变量作为变量名?”的问题,但可能没有。)
在任何情况下,您都应该能够将整个子例程分配给变量(作为参考),然后直接调用它:
# 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"
}
最重要的是:为什么要将变量用作函数名。 如果它是'eval'会发生什么? 是否有可以使用的功能列表? 或者它可以是任何功能? 如果列表存在 - 它有多长?
通常,处理此类情况的最佳方法是使用调度表:
my %dispatch = (
'addition' => \&some_addition_function,
'multiplication' => sub { $self->call_method( @_ ) },
);
然后就是:
$dispatch{ $your_variable }->( 'any', 'args' );
__PACKAGE__->can($action)->(@args);
有关can()的更多信息: http : //perldoc.perl.org/UNIVERSAL.html
我做了类似的事情。 我把它分成两行,使它更容易识别,但它并不是很漂亮。
my $sub = \&{$action};
$sub->();
我不知道更正确或更漂亮的方式。 对于它的价值,我们有生产代码可以完成您正在做的事情,并且无需禁用use strict
。
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 。
要么使用
&{\&{$action}}();
或者使用eval执行该功能:
eval("$action()");
我是这样做的:
@func = qw(cpu mem net disk);
foreach my $item (@func){
$ret .= &$item(1);
}
如果只在一个程序中,编写一个使用变量名称调用子程序的函数,只需记录一次/道歉一次?
我用过这个:它对我有用。
(\$action)->();
或者您可以使用'do',与以前的帖子非常相似:
$p = do { \&$conn;};
$p->();
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.