简体   繁体   English

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

[英]How can I elegantly call a Perl subroutine whose name is held in a variable?

I keep the name of the subroutine I want to call at runtime in a variable called $action. 我保留了我想在运行时在名为$ action的变量中调用的子例程的名称。 Then I use this to call that sub at the right time: 然后我用它在正确的时间调用该子:

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

Works fine. 工作良好。 The only thing I don't like is that it's ugly and every time I do it, I feel beholden to add a comment for the next developer: 我唯一不喜欢的是它很丑陋,每次我这样做,我都感到很高兴为下一个开发者添加评论:

# call the sub by the name of $action

Anyone know a prettier way of doing this? 有人知道更漂亮的方式吗?


UPDATE: The idea here was to avoid having to maintain a dispatch table every time I added a new callable sub, since I am the sole developer, I'm not worried about other programmers following or not following the 'rules'. 更新:这里的想法是避免每次添加新的可调用子时都必须维护一个调度表,因为我是唯一的开发人员,我不担心其他程序员遵循或不遵循“规则”。 Sacrificing a bit of security for my convenience. 为了方便我牺牲了一点安全性。 Instead my dispatch module would check $action to make sure that 1) it is the name of a defined subroutine and not malicious code to run with eval, and 2) that it wouldn't run any sub prefaced by an underscore, which would be marked as internal-only subs by this naming convention. 相反,我的调度模块将检查$ action以确保1)它是已定义的子例程的名称而不是与eval一起运行的恶意代码,以及2)它不会运行任何以下划线开头的子,这将是通过此命名约定标记为仅限内部的subs。

Any thoughts on this approach? 有关这种方法的任何想法? Whitelisting subroutines in the dispatch table is something I will forget all the time, and my clients would rather me err on the side of "it works" than "it's wicked secure". 发送表中的白名单子程序是我一直都会忘记的东西,我的客户宁愿我在“它工作”而不是“它是邪恶的安全”方面犯错误。 (very limited time to develop apps) (开发应用程序的时间非常有限)


FINAL UPDATE: I think I've decided on a dispatch table after all. 最后更新:我想我毕竟已经决定了一个调度表。 Although I'd be curious if anyone who reads this question has ever tried to do away with one and how they did it, I have to bow to the collective wisdom here. 虽然我很好奇,如果读过这个问题的人曾经试图废除一个以及他们是如何做到的,那么我不得不向这里的集体智慧屈服。 Thanks to all, many great responses. 感谢所有,许多伟大的回应。

Rather than storing subroutine names in a variable and calling them, a better way to do this is to use a hash of subroutine references (otherwise known as a dispatch table .) 不是将子例程名称存储在变量中并调用它们,更好的方法是使用子例程引用的哈希(也称为分派表)

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

Then you can call the right one easily: 然后你可以轻松地拨打正确的电话:

$actions{$action}->();

You can also add some checking to make sure $action is a valid key in the hash, and so forth. 您还可以添加一些检查以确保$action是哈希中的有效键,依此类推。

In general, you should avoid symbolic references (what you're doing now) as they cause all kinds of problems. 一般来说,你应该避免使用符号引用(你现在正在做什么),因为它们会导致各种各样的问题。 In addition, using real subroutine references will work with strict turned on. 此外,使用实际子例程引用将strict打开。

Just &$action() , but usually it's nicer to use coderefs from the beginning, or use a dispatcher hash. 只是&$action() ,但通常从一开始就使用coderefs更好,或使用调度程序哈希。 For example: 例如:

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

Huh? 咦? You can just say 你可以说

    $action->()

Example: 例:

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


    $ perl subfromscalar.pl
    11

Constructions like 像。这样的结构

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

also work. 也工作。

I'm not sure I understand what you mean. 我不确定我明白你的意思。 (I think this is another in a recent group of "How can I use a variable as a variable name?" questions, but maybe not.) (我认为这是最近一组“如何使用变量作为变量名?”的问题,但可能没有。)

In any case, you should be able to assign an entire subroutine to a variable (as a reference), and then call it straightforwardly: 在任何情况下,您都应该能够将整个子例程分配给变量(作为参考),然后直接调用它:

# 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"
}

The most important thing is: why do you want to use variable as function name. 最重要的是:为什么要将变量用作函数名。 What will happen if it will be 'eval'? 如果它是'eval'会发生什么? Is there a list of functions that can be used? 是否有可以使用的功能列表? Or can it be any function? 或者它可以是任何功能? If list exists - how long it is? 如果列表存在 - 它有多长?

Generally, the best way to handle such cases is to use dispatch tables: 通常,处理此类情况的最佳方法是使用调度表:

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

And then just: 然后就是:

$dispatch{ $your_variable }->( 'any', 'args' );
__PACKAGE__->can($action)->(@args);

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

I do something similar. 我做了类似的事情。 I split it into two lines to make it slightly more identifiable, but it's not a lot prettier. 我把它分成两行,使它更容易识别,但它并不是很漂亮。

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

I do not know of a more correct or prettier way of doing it. 我不知道更正确或更漂亮的方式。 For what it's worth, we have production code that does what you are doing, and it works without having to disable use strict . 对于它的价值,我们有生产代码可以完成您正在做的事情,并且无需禁用use strict

Every package in Perl is already a hash table. Perl中的每个包都已经是一个哈希表。 You can add elements and reference them by the normal hash operations. 您可以通过常规哈希操作添加元素并引用它们。 In general it is not necessary to duplicate the functionality by an additional hash table. 通常,没有必要通过附加的哈希表来复制功能。

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

my $tag = 'HTML';

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

HTML("body1");

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

The code prints: 代码打印:

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

If you need a separate name space, you can define a dedicated package. 如果需要单独的名称空间,则可以定义专用包。

See perlmod for further information. 有关详细信息,请参阅perlmod

Either use 要么使用

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

Or use eval to execute the function: 或者使用eval执行该功能:

eval("$action()");

I did it in this way: 我是这样做的:

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

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

I used this: it works for me. 我用过这个:它对我有用。

(\$action)->();

Or you can use 'do', quite similar with previous posts: 或者您可以使用'do',与以前的帖子非常相似:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM