Perl子程序原型 - 正确的方法

时间:2011-11-14 22:13:20

标签: perl

我有一个名为debug的子程序,我在我的代码中使用。它基本上可以让我看到发生了什么等等。

sub debug {
    my $message      = shift;
    my $messageLevel = shift;

    our $debugLevel;
    $messageLevel = 1 if not defined $messageLevel;
    return if $messageLevel > $debugLevel;
    my $printMessage = "    " x $messageLevel . "DEBUG: $message\n";
    print STDERR $printMessage;
    return $printMessage;
}

我想对此进行原型设计,所以我可以做这样的事情:

debug "Here I am! And the value of foo is $foo";

debug "I am in subroutine foo", 3;

同时,我喜欢将子程序定义放在我程序的底部,所以你不必在代码中用1/2来找到程序的内容。

我想这样做:

sub debug($;$);  #Prototype debug subroutine

/Here goes the main program code/

sub debug {      #The entire subroutine goes here
   /Here goes the debug subroutine code/
}

但是,当我这样做时,我会收到警告:

Prototype mismatch: sub main::debug ($;$) vs none at foo.pl line 249.

所以,我坚持把原型定义放在两个地方。做这样的事的正确方法是什么?


RESPONSE

  

停止!模块时间。 - 克里斯卢茨

一个模块?你是说创建一个单独的文件?这增加了一些复杂性而没有解决我试图解决的问题:删除围绕这个特定子例程的括号的需要。

  

我们的$ debugLevel;不管怎么说,不应该在子体内,但我同意克里斯的观点。 - SinanÜnür3小时前

在这种情况下our $debugLevel不一定存在,但如果我定义了一个类,并且我想在我的类中使用这个子例程进行调试,我需要它。我可以把它作为::debug

列入我的班级
  

令人惊讶的是,Far more than everything you ever wanted to know about prototypes in Perl没有解决这个问题,但我相信你无法避免在两个地方都写出原型。

我希望有一种简单的方法来避免它。 Eric Strom表明了一种方式。不幸的是,它比debug例程更长。

  

我曾经使用原型,但我养成了不为子程序编写单独声明并在所有调用中使用括号的习惯:debug(“我在子程序foo”,3);.有人认为原型确实不是一个好主意。 TMTOWTDI - Keith Thompson 3小时

除了我倾向于这样做:

debug (qq(The value of Foo is "$foo"), 3);

在阅读时可能不太清楚,并且可能很难打字。每当你加倍括号时,你就会遇到麻烦。我想做的最后一件事是调试我的调试语句。

  

为什么要原型?请参阅此问题How to pass optional parameters to a Perl function - TLP

是的,原型制作存在很多问题。主要的问题是它根本不做人们认为它应该做的事情:声明你传递给函数的参数的变量类型。

这不是我在这里使用原型的原因。

我很少使用原型。事实上,这可能是我所有代码中唯一的情况。

5 个答案:

答案 0 :(得分:26)

完全摆脱原型:

sub debug;

debug "Here I am! And the value of foo is $foo";
debug "I am in subroutine foo", 3;

sub debug {
    # body of debug
}

答案 1 :(得分:6)

原型附加到coderef而不是名称,因此当您使用新声明替换coderef时,您正在清除原型。您可以避免使用辅助函数交叉引用和匹配原型:

sub debug ($;$);

debug 'foo';

use Scalar::Util 'set_prototype';
sub install {
    my ($name, $code) = @_;
    my $glob = do {no strict 'refs'; \*$name};
    set_prototype \&$code, prototype \&$glob;
    *$glob = $code;
}

BEGIN {
    install debug => sub {
        print "body of debug: @_\n";
    };
}

install只是Scalar::Util的{​​{1}}函数的包装器,它允许您在创建coderef之后更改其原型。

原型可能非常有用,但在使用标量原型时,请始终问自己这是否真的符合您的意图。因为set_prototype原型告诉perl“debug是一个可以带一个或两个参数的函数,每个参数都有一个强加于调用站点的标量上下文”。

关于背景的一点是人们通常被绊倒的地方,因为如果你试图这样做:

($;$)

然后在标量上下文中看到my @array = qw(one two); debug @array; ,并变为@array。因此,调用变为2而非debug 2;,如您所料。

答案 2 :(得分:3)

如果我理解正确,您需要原型和预先声明,以便您可以在同一文件中使用该功能(原型和无支撑)。这就是subs编译指示的用途。

例如,此代码可以正常工作:

#!/usr/bin/env perl

use strict;
use warnings;

use subs qw/mysay/;

mysay "Yo";
mysay "Yo", "Joel";

sub mysay ($;$) {
  my $message = shift;
  my $speaker = shift;
  if (defined $speaker) {
    $message = "$speaker says: " . $message;
  }
  print $message, "\n";
}

答案 3 :(得分:1)

定义子程序时必须声明相同的原型:

sub debug($;$); # prototype/declare

... meat of the program ...

sub debug($;$) {
    ...
}

答案 4 :(得分:0)

以下是如何操作:

sub debug;  #Prototype debug subroutine

#Here goes the main program code/

sub debug($;$) {
   #Here goes the debug subroutine code/
}