【问题标题】:Perl Subroutine Prototyping -- The correct way to do itPerl 子程序原型——正确的方法
【发布时间】:2011-12-29 01:24:50
【问题描述】:

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

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


回应

停下!模块时间。 ——克里斯·卢茨

一个模块?你的意思是创建一个单独的文件?这增加了一些复杂性,但没有解决我要解决的问题:消除对这个特定子例程周围括号的需求。

我们的 $debugLevel;无论如何不应该在子正文中,但我同意克里斯的观点。 – Sinan Ünür 3 小时前

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

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

我希望有一个简单的方法来避免它。 Eric Strom 展示了一种方法。不幸的是,它比我的debug 例程要长。

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

除了我会倾向于这样做:

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

阅读时可能不太清楚,而且打字会很痛苦。每当你把括号加倍时,你就是在自找麻烦。我要做的最后一件事是调试我的调试语句。

为什么需要原型?看到这个问题How to pass optional parameters to a Perl function – TLP

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

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

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

【问题讨论】:

  • our $debugLevel; 无论如何都不应该出现在子正文中,但我同意 Chris 的观点。
  • 令人惊讶的是,perlmonks.org/?node_id=861966 没有解决这个问题,但我相信你不能避免在这两个地方编写原型。
  • 我曾经使用原型,但我养成了为子例程编写单独声明并在所有调用中使用括号的习惯:debug("I am in subroutine foo", 3);。有人建议原型真的不是一个好主意。 TMTOWTDI
  • 为什么需要原型?看到这个最近的问题stackoverflow.com/q/8124138/725418
  • 在我的原始帖子末尾查看我的 RESPONSE

标签: perl


【解决方案1】:

完全摆脱原型:

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
}

【讨论】:

  • 完全同意。 Perl 中的原型的使用方式与其他语言中的不同,并且对于与其他语言中相同的事情没有用处。请参阅上面 ehemient 评论中的链接。
  • 除了在这种情况下,我将原型用于其预期目的:允许我使用不带括号的子例程,就像使用内置函数一样。你不应该将原型用于类型检查。
  • @David W - 这是错误的。它不是使括号可选的原型。
  • @David - mob 是正确的。使括号成为可选的事情是声明debug 是一个子例程。
  • 该死的。我一直认为你必须使用原型来消除括号。
【解决方案2】:

原型附加到 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::Utilset_prototype 函数的包装器,它允许您在创建coderef 后更改它的原型。

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

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

my @array = qw(one two);

debug @array;

然后@array 在标量上下文中被看到,并变成2。所以调用变成了debug 2;,而不是你可能预期的debug 'one', 'two';

【讨论】:

  • 是的,我知道原型设计的局限性。在这种情况下,我几乎按照预期使用它。我希望能够输入 debug "Some statement"; 并可能附加一个调试级别,如 debug "Some statement", 2; 而不必担心括号。辅助函数很有趣,但它比我的小调试子程序还要长。尽管如此,我还是喜欢它,尽管我可能会像以前那样做。
  • 是的,关于debug @array 的这个特殊小问题。问题是我有时想包含一个调试级别,但不一定。如果我传入@array,我不知道那是一个数组,还是一个最后带有调试级别的数组。我知道我传递给例程的内容必须是标量。
  • 很公平。如果没有关于原型的任何免责声明,SO 警察将在没有警告的情况下拒绝投票。事实上,我想我在打字时被击中了......
  • +1 用于提及Scalar::Util 'set_prototype'。这是我遇到问题的关键,这导致我写了这篇 SO 帖子。 :-)
【解决方案3】:

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

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

#!/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";
}

【讨论】:

  • 那么,我可以摆脱原型了。我认为我必须使用它们的唯一原因是使用不带括号的函数。
  • @DavidW。不,它所需要的只是预先声明。原型在函数之后强制使用上下文或列表的其他特定用途。更多信息请参阅 [perldoc perlsub],“此功能的目的主要是让您定义像内置函数一样工作的子例程”(perldoc.perl.org/perlsub.html#Prototypes)
  • @DavidW。一个很好的例子是,如果您需要一个函数来获取两个不同的数组,而不是像push 那样将它们展平成一个列表。大多数时候,人们会通过让函数获取数组引用而不是数组来做到这一点,但如果你想要一个更简洁的界面,那么原型就是答案。你应该很少需要它们。
【解决方案4】:

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

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

... meat of the program ...

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

【讨论】:

  • 作者在问题中暗示他知道这一点。另外,我认为有一种方法可以在不复制原型的情况下做到这一点。
【解决方案5】:

这是怎么做的:

sub debug;  #Prototype debug subroutine

#Here goes the main program code/

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

【讨论】:

    猜你喜欢
    • 2012-09-17
    • 2023-03-26
    • 1970-01-01
    • 1970-01-01
    • 2015-08-05
    • 2013-12-31
    • 2010-11-15
    • 1970-01-01
    • 2012-01-24
    相关资源
    最近更新 更多