Perl子例程原型——正确的方法

Perl子例程原型——正确的方法,perl,Perl,我在代码中使用了一个名为debug的子程序。它基本上可以让我看到发生了什么,等等 sub debug { my $message = shift; my $messageLevel = shift; our $debugLevel; $messageLevel = 1 if not defined $messageLevel; return if $messageLevel > $debugLevel; my $printMess

我在代码中使用了一个名为
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";

同时,我喜欢将子程序定义放在程序的底部,这样您就不必费力地遍历代码来找到程序的核心部分

我想这样做:

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

/Here goes the main program code/

sub debug {      #The entire subroutine goes here
   /Here goes the debug subroutine code/
}
my @array = qw(one two);

debug @array;
但是,当我这样做时,我会得到一个警告:

Prototype mismatch: sub main::debug ($;$) vs none at foo.pl line 249.
所以,我坚持把原型定义放在两个地方。这样做的正确方法是什么


回应 住手!模块时间–克里斯·卢茨

模块?你是说创建一个单独的文件?这在没有解决我试图解决的问题的情况下增加了一点复杂性:消除了在这个特定子例程周围使用括号的需要

我们的美元水平;无论如何都不应该在子机构中,但我同意Chris的观点三小时前的西南努尔

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

令人惊讶的是,它没有解决这个问题,但我相信您无法避免在这两个地方编写原型

我希望有一个简单的方法来避免它。埃里克·斯特罗姆(Eric Strom)展示了一种方法。不幸的是,它比我的
debug
例程要长


我曾经使用原型,但我已经养成了不为子例程编写单独声明和在所有调用中使用括号的习惯:debug(“我在子例程foo中”,3);。有人认为原型并不是一个好主意。TMTOWTDI–基思·汤普森3小时

除非我倾向于:

debug (qq(The value of Foo is "$foo"), 3);
这在阅读时可能不太清晰,而且打字也会很痛苦。每当你把括号括起来,你就是在自找麻烦。我最不想做的事情就是调试调试语句


你为什么想要原型?请参阅此问题–TLP

是的,原型设计有很多问题。主要的问题是它根本没有做人们认为它应该做的事情:为传递给函数的参数声明变量类型

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


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

在定义子例程时,必须声明相同的原型:

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

... meat of the program ...

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

完全摆脱原型:

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
}

原型附加到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
set\u prototype
函数的包装,它允许您在创建coderef的原型后更改原型

原型可能非常有用,但当使用标量原型时,请始终扪心自问,这是否真的是您想要的。因为
($;$)
原型告诉perl“debug是一个可以接受一个或两个参数的函数,每个参数都在调用站点上施加标量上下文”

上下文是人们经常被绊倒的地方,因为如果你尝试这样做:

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

/Here goes the main program code/

sub debug {      #The entire subroutine goes here
   /Here goes the debug subroutine code/
}
my @array = qw(one two);

debug @array;

然后在标量上下文中看到
@array
,并变成
2
。因此调用变成
debug 2而不是
调试“一”、“二”如您所料。

如果我理解正确,您希望原型化和预声明,以便可以在同一文件中使用函数(原型化和无括号)。这就是布拉格语的意义

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

#!/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";
}
以下是如何做到这一点:

sub debug;  #Prototype debug subroutine

#Here goes the main program code/

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

我们的$debugLevel无论如何不应该在子体中,但我同意Chris的观点。令人惊讶的是,它没有解决这个问题,但我相信您无法避免在这两个地方编写原型。我曾经使用原型,但我养成了不为子例程编写单独声明和在所有调用中使用括号的习惯:
debug(“我在子程序foo中”,3)
。有人建议原型确实不是一个好主意。你为什么想要原型?请看最近的这个问题。请看我在原始帖子末尾的回答。我完全同意。Perl中的原型与其他语言中的使用方式不同,并且与其他语言中的原型不适用于相同的事情。请参阅我的链接n ephemient的评论,如上所述。除本例外,我使用原型的目的是:允许我使用不带括号的子例程,就像使用内置函数一样。不应该使用原型进行类型检查。@David W-这是错误的。它不是使括号可选的原型。@David-mob是正确的。让括号成为可选的东西是声明
debug
是一个子程序。该死。我一直认为你必须使用原型来消除括号。是的,我知道原型的局限性。在这种情况下,我使用它的目的非常明确。我想能够键入
debug”一些语句";并可能附加一个调试级别,如
调试“Some statement”,2而不用担心括号。helper函数很有趣,但它甚至比我的小调试子例程还要长。尽管如此,我还是很喜欢它,尽管我可能会像以前那样做。是的,我对
debug@array
的这个小问题很感兴趣。问题是我有时想包括调试级别,但不一定。如果我传入@array,我不知道这是一个数组,还是一个最后有调试级别的数组。我知道我传递给例程的一定是一个标量,这很公平。如果没有关于regar的免责声明,那么警方将在没有任何警告的情况下击落选票