我什么时候应该使用&调用Perl子程序?

时间:2009-08-28 14:32:59

标签: perl subroutine

我听说人们不应该使用&来调用Perl subs,即:

function($a,$b,...);
# opposed to
&function($a,$b,...);

我知道一个参数列表变成了可选项,但是在哪些情况下使用&以及你绝对不应该使用它的情况呢?

当省略&

时,性能增加如何发挥作用呢?

4 个答案:

答案 0 :(得分:52)

我经常滥用&,但主要是因为我做了奇怪的界面内容。如果您不需要其中一种情况,请不要使用&。其中大多数只是访问子程序定义,而不是调用子程序。全部都在perlsub

  1. 引用已命名的子例程。这可能是大多数Perlers的唯一常见情况:

     my $sub = \&foo;
    
  2. 同样,分配给typeglob,它允许您使用不同的名称调用子例程:

     *bar = \&foo;
    
  3. 检查是否定义了子例程,就像在测试套件中一样:

     if( defined &foo ) { ... }
    
  4. 删除一个不常见的子程序定义:

     undef &foo;
    
  5. 提供一个调度程序子程序,其唯一的工作是选择要调用的正确子程序。这是我使用&调用子程序的唯一情况,当我希望多次调用调度程序时,需要从操作中挤出一点性能:

     sub figure_it_out_for_me {
        # all of these re-use the current @_
          if( ...some condition... ) { &foo     } 
       elsif( ...some other...     ) { &bar     }
       else                          { &default }
       }
    
  6. 使用当前参数堆栈跳转到另一个子例程(并替换调用堆栈中的当前子例程),在调度中执行一个unrare操作,尤其是AUTOLOAD

     goto ⊂
    
  7. 调用您在内置Perl后命名的子例程。 &始终为您提供用户定义的一个。那是why we teach it in Learning Perl。你真的不想这样做,但它是&的一个特征。

  8. 有些地方可以使用它们,但有更好的方法:

    1. 调用与内置Perl同名的子例程。只是没有与内置Perl同名的子程序。检查perlfunc以查看不应使用的内置名称列表。

    2. 禁用原型。如果您不知道这意味着什么或为什么需要它,请不要使用&。一些黑魔法代码可能需要它,但在这些情况下你可能知道你在做什么。

    3. 取消引用并执行子程序引用。只需使用->表示法。

答案 1 :(得分:36)

IMO,唯一有理由使用&的是您获取或调用coderef,如:

sub foo() {
    print "hi\n";
}

my $x = \&foo;
&$x();

在大多数情况下绝对不应该的主要时间是在调用具有指定任何非默认调用的原型的sub时行为。我的意思是,一些原型允许重新解释参数列表,例如将@array%hash规范转换为引用。因此,sub会期望这些重新解释已经发生,除非你去手动模仿它们所需的任何长度,否则sub将获得与它预期的输入大不相同的输入。

我认为主要是人们试图告诉你,你仍然用Perl 4风格写作,现在我们有了一个更干净,更好的东西叫做Perl 5。

关于性能,Perl有多种方法可以优化&失败的子调用,其中一个主要是内联常量内联。

还有一种情况是,使用&可以提供性能优势:如果您使用foo(@_)转发子呼叫。使用&foo的速度比foo(@_)快得多。除非你通过剖析确定你需要微观优化,否则我不会推荐它。

答案 2 :(得分:17)

& subroutine()表单禁用原型检查。这可能是也可能不是你想要的。

http://www.perl.com/doc/manual/html/pod/perlsub.html#Prototypes

Prototypes允许您指定子例程参数的数量和类型,并在编译时检查它们。这可以提供有用的诊断帮助。

原型不适用于方法调用,也不适用于使用&amp ;;前缀。

&有必要引用或取消引用子例程或代码引用

e.g。

sub foo {
   # a subroutine
}

my $subref = \&foo; # take a reference to the subroutine

&$subref(@args);  # make a subroutine call using the reference.

my $anon_func = sub { ... }; # anonymous code reference
&$anon_func(); # called like this

原型也不适用于子程序引用。

&子程序表格也用于所谓的magic goto形式。

表达式goto &subroutine使用@_的当前值替换当前调用上下文,并调用named子例程。

实质上,您可以通过调用指定的子程序将调用完全切换到一个子程序。这在AUTOLOAD块中很常见,其中可以进行延迟子例程调用,可能对@_进行一些修改,但它看起来完全像程序调用命名子。

e.g。

sub AUTOLOAD {
    ...
    push @_, @extra_args; # add more arguments onto the parameter list
    goto &subroutine ; # change call another subroutine, as if we were never here
}

}

我认为这可能对tail call elimination有用。

请参阅detailed explanation of this technique here

答案 3 :(得分:1)

我已经阅读了反对使用'&'的论点,但我几乎总是使用它。它给我节省了太多时间。我花费了很大一部分Perl编码时间来查找代码的哪些部分调用特定函数。有了领先优势,我可以立即搜索并找到它们。如果没有前导&,我会得到函数定义,注释和调试语句,通常需要检查的代码量增加三倍才能找到我想要的内容。

主要不使用'&'买你是它让你使用功能原型。但Perl函数原型可能会在它们阻止它们时产生错误,因为它们将采用您的参数列表并以您可能不期望的方式重新解释它,以便您的函数调用不再传递它实际上所说的参数。