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

Dav*_* W. 7 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;
}
Run Code Online (Sandbox Code Playgroud)

我想要原型,所以我可以做这样的事情:

debug "Here I am! And the value of foo is $foo";
Run Code Online (Sandbox Code Playgroud)

要么

debug "I am in subroutine foo", 3;
Run Code Online (Sandbox Code Playgroud)

同时,我喜欢将子程序定义放在我的程序的底部,所以你不必在代码中用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/
}
Run Code Online (Sandbox Code Playgroud)

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

Prototype mismatch: sub main::debug ($;$) vs none at foo.pl line 249.
Run Code Online (Sandbox Code Playgroud)

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


响应

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

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

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

our $debugLevel没有必须有在这种情况下,但如果我定义一个类,我想在我的类中使用这个子程序进行调试,我需要它.我可以把它作为我的班级::debug

令人惊讶的是,你所知道的关于Perl原型的所有内容都不能解决这个问题,但我相信你无法避免在这两个地方编写原型.

我希望有一个简单的方法来避免它.Eric Strom表明了一种方式.不幸的是,它比我的debug日常工作更长.

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

除了我倾向于:

debug (qq(The value of Foo is "$foo"), 3);
Run Code Online (Sandbox Code Playgroud)

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

你为什么要原型?请参阅此问题如何将可选参数传递给Perl函数 - TLP

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

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

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

Ted*_*opp 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
}
Run Code Online (Sandbox Code Playgroud)

  • @David W--这是错的.它不是使括号可选的原型. (7认同)
  • @David - 暴徒是正确的。使括号可选的是声明 `debug` 是一个子程序。 (2认同)

Eri*_*rom 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";
    };
}
Run Code Online (Sandbox Code Playgroud)

install是只是一个包装周围Scalar::Utilset_prototype功能,它允许在创建后您更改CODEREF的原型.

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

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

my @array = qw(one two);

debug @array;
Run Code Online (Sandbox Code Playgroud)

然后@array在标量上下文中看到,并成为2.因此,呼叫变得debug 2;不像debug 'one', 'two';您预期的那样.


Joe*_*ger 5

如果我理解正确,您想要原型化和预先声明,以便您可以在同一文件中使用该函数(原型化和无支撑)。这就是subspragma 的用途。

例如,此代码正常工作:

#!/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";
}
Run Code Online (Sandbox Code Playgroud)