52

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

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

我知道其中一个参数列表是可选的,但是在哪些情况下适合使用它,而&在哪些情况下你绝对不应该使用它?

另外,当省略&?

4

4 回答 4

55

我经常滥用&,但主要是因为我正在做奇怪的界面东西。如果您不需要其中一种情况,请不要使用&. 其中大部分只是访问子程序定义,而不是调用子程序。这一切都在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. 要使用当前参数堆栈跳转到另一个子程序(并替换调用堆栈中的当前子程序),这是调度中罕见的操作,尤其是在AUTOLOAD

     goto ⊂
    
  7. 调用一个以 Perl 内置函数命名的子例程。&总是给你用户定义的。这就是我们在Learning Perl中教授它的原因。您并不想正常执行此操作,但这是&.

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

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

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

  3. 取消引用并执行子例程引用。只需使用->符号。

于 2009-08-28T19:37:44.427 回答
38

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

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

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

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

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

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

还有一种情况下 using&可以提供性能优势:如果您使用 . 转发子呼叫foo(@_)。使用&foo速度比foo(@_). 我不会推荐它,除非你通过分析明确发现你需要那个微优化。

于 2009-08-28T14:35:32.100 回答
18

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

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

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

原型不适用于方法调用或使用 & 前缀以老式风格进行的调用。

& 是引用或取消引用子例程或代码引用所必需的

例如

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

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

&subroutine 形式也用于所谓的魔术 goto形式。

该表达式goto &subroutine使用@_ 的当前值将当前调用上下文替换为对命名子例程的调用。

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

例如

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

}

我想这可能对消除尾调用很有用。

在此处查看此技术的详细说明

于 2009-08-28T14:39:42.017 回答
1

我已经阅读了反对使用“&”的论点,但我几乎总是使用它。它节省了我太多的时间。我花了很大一部分 Perl 编码时间来寻找代码的哪些部分调用了特定的函数。使用前导&,我可以立即搜索并找到它们。如果没有前导 &,我会得到函数定义、注释和调试语句,这通常会使我必须检查的代码量增加三倍以找到我正在寻找的内容。

不使用 '&' 的主要好处是它可以让你使用函数原型。但是 Perl 函数原型可能会像阻止错误一样频繁地创建错误,因为它们会获取您的参数列表并以您可能意想不到的方式重新解释它,因此您的函数调用不再传递它字面上所说的参数。

于 2015-03-11T03:35:10.020 回答