2010-05-16 61 views
4

在我的Perl代码中,我以前使用过以下两种写法,不鼓励在现代perl:

# Style #1: Using & before calling a user-defined subroutine 
&name_of_subroutine($something, $something_else); 

# Style #2: Using ($$) to show the number of arguments in a user-defined sub 
sub name_of_subroutine($$) { 
    # the body of a subroutine taking two arguments. 
} 

由于知道这些样式不建议我已经停止使用它们。

然而,出于好奇,我想知道以下几点:

  • 什么是这两种写作风格的由来? (我敢肯定,我自己并没有梦想过这种风格。)
  • 为什么现代perl中的这两种写作风格不受欢迎?
  • 风格在某个时间点被认为是最佳实践吗?
+1

所有已经讨论过的SO:http://stackoverflow.com/questions/2056904 http:// stackoverflow。com/questions/297034 http://stackoverflow.com/questions/2485078 – daxim 2010-05-16 15:45:54

+1

[为什么Perl函数原型不好?](http://stackoverflow.com/questions/297034/why-are-perl-function- prototypes-bad) – Ether 2010-05-16 16:42:19

+2

由于'&foo(@args)'的使用覆盖了原型,所以你会问这两种常见的Perl特性滥用问题,这很具有讽刺意味。 – daotoad 2010-05-16 18:24:45

回答

15

&由于两个原因,sigil不常用于现代Perl中的函数调用。首先,它很大程度上是多余的,因为Perl会考虑任何看起来像函数(随后是parens)的函数。其次,执行&function()&function的方式有很大的不同,这可能会让经验较少的Perl程序员感到困惑。在第一种情况下,函数被调用时没有参数。在第二种情况下,调用函数与当前@_(它甚至可以更改参数列表将被后来的语句在范围中可以看出:

sub print_and_remove_first_arg {print 'first arg: ', shift, "\n"} 

sub test { 
    &print_and_remove_first_arg; 

    print "remaining args: @_\n"; 
} 

test 1, 2, 3; 

打印

first arg: 1 
remaining args: 2 3 

因此最终,对每个函数调用使用&最终隐藏了几个&function;调用,这会导致难以发现的错误。另外,使用& sigil可以防止遵守函数原型,这在某些情况下很有用(如果您知道你在做什么),但也可能导致困难追踪错误。最终,&是功能行为的强大修饰符,只应在需要该行为时使用。

原型是相似的,它们的使用应该限制在现代Perl中。必须明确说明的是,Perl中的原型不是函数签名。它们提示编译器告诉它以与内置函数类似的方式解析对这些函数的调用。也就是说,原型中的每个符号都会告诉编译器在参数上强加这种类型的上下文。此功能在定义类似于mappush或的函数时非常有用,它们的第一个参数与标准列表运算符的区别不同。

sub my_map (&@) {...} # first arg is either a block or explicit code reference 

my @ret = my_map {some_function($_)} 1 .. 10; 

原因原型sub ($$) {...}和类似用途都望而却步,因为9次了10年笔者的意思是“我想有两个参数”,而不是“我希望每个有两个参数与施加在调用点标量上下文” 。前者主张更好地写着:

use Carp; 
sub needs2 { 
    @_ == 2 or croak 'needs2 takes 2 arguments'; 
    ... 
} 

这将然后允许如预期的那样在呼叫风格的工作:

my @array = (2, 4); 

needs2 @array; 

综上所述,无论是&印记和函数原型是有用和强大的工具,但只有在需要该功能时才能使用它们。他们多余的使用(或误用作参数验证)会导致意外的行为,并且很难追踪错误。

+3

如果你投票回答,解释为什么... – 2010-05-16 20:12:51

1

对于风格#1,&子程序之前只有必要的,如果你有一个共享的名称和内建的子程序,你需要消除歧义,你想哪一个打电话,让翻译知道这是怎么回事上。否则,相当于调用不含&的子程序。

既然如此,我会说它的使用是不鼓励的,因为你不应该命名你的子程序的名字与builtins的名字相同,并且在你调用它们之前定义你所有的子程序是很好的做法,的阅读理解。除此之外,如果您在调用子程序之前先定义子程序,则可以省略括号,就像在内建中一样。另外,只是直观地讲,在每个子程序前坚持&不必要地混淆了文件。

至于函数原型,事后他们被困在Perl中,并没有真正做他们做的事情。从an article perl.com:

大多数情况下,原型比他们的价值更麻烦。首先,Perl不会检查方法的原型,因为这需要能够在编译时确定哪个类将处理该方法。因为你可以在运行时改变@ISA - 你会发现问题。然而,主要原因是原型不太聪明。如果指定sub foo($$$),则不能将它传递给三个标量数组(这是vec()的问题)。相反,你必须说foo($ x [0],$ x [1],$ x [2]),这只是一个痛苦。

最后,最好是注释你的代码,以表明你打算让子程序接受并自己进行参数检查。正如文章所述,这对类方法实际上是必需的,因为它们没有参数检查。

对于它的价值,Perl 6的补充形式参数列表,以这样的语言:

sub do_something(Str $thing, Int $other) { 
    ... 
} 
+2

在文件中定义之前,您不必包括&当您进行子程序调用时。子程序名称空间在编译时填充。 – Brock 2010-05-16 13:07:27

+0

你是对的,我实际上正在考虑在调用之前放置声明时忽略括号的能力。 http://oreilly.com/catalog/lperl3/chapter/ch04.html中的“省略&符号”给出了简要说明。 – v64 2010-05-16 13:38:43

+0

这不就是为什么我们有'CORE :: func'吗? – rjh 2010-05-16 13:55:33

3

在函数调用的&在Perl 4是强制性的,所以也许你有回升,最多从编程的Perl (1991)由拉里沃尔和兰德尔施瓦茨,像我这样做,或在某处类似。

至于函数原型,我的猜测是不合格的。也许你一直在模仿有意义的语言和/或强制声明参数列表,并且因为Perl中的函数原型看起来有点像参数列表,你已经开始添加它们了?

&功能不鼓励,因为它使代码的可读性,而不是必要的(在该情况下功能&是必要的是罕见的和往往更好避免)。

函数原型不是参数列表,所以大多数时候他们只会混淆读者,或者让你陷入僵化的错觉,所以除非你确切地知道你是什么,否则不需要使用它们。

&在Perl 4中是必需的,所以它们是最好的/必要的练习。我不认为功能原型曾经是。

+3

你忘了提及使用sigil改变sub的调用方式。 '&foo(@args)'禁用原型。 '&foo'(无参数)将当前参数列表('_ _')直接传递给子例程。 – daotoad 2010-05-16 18:22:43