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

发布于 2024-12-15 15:52:15 字数 2213 浏览 0 评论 0原文

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

令人惊讶的是,远比你想了解的关于 Perl 原型的所有内容没有解决这个问题,但我相信你无法避免在这两个地方编写原型。

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

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

除了我会倾向于这样做:

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

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

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

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

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

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

I have a subroutine called debug I use in my code. It basically allows me to see what's going on, etc.

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;
}

I want to prototype this, so I can do things like this:

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

or

debug "I am in subroutine foo", 3;

At the same time, I like putting subroutine definitions at the bottom of my program, so you don't have to wade 1/2 way through the code to find the meat of the program.

I'd like to do this:

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

/Here goes the main program code/

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

However, I get a warning when I do this:

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

So, I'm stuck putting the prototype definition in both places. What is the correct way to do something like this?


RESPONSE

Stop! Module time. – Chris Lutz

A module? You mean create a separate file? That adds a bit of complication without solving the issue I'm trying to solve: Removing the need for parentheses around this particular subroutine.

our $debugLevel; should not be in the sub body anyway, but I agree with Chris on this. – Sinan Ünür 3 hours ago

The our $debugLevel does not have to be there in this case, but if I defined a class and I want to use this subroutine in my class for debugging, I need it. I can put it in my class as ::debug

Surprisingly, Far more than everything you ever wanted to know about prototypes in Perl doesn't address this, but I believe you cannot avoid writing the prototype in both places.

I was hoping for an easy way to avoid it. There is a way as Eric Strom showed. Unfortunately, it's longer than my debug routine.

I used to use prototypes, but I've developed the habit of not writing separate declarations for subroutines and using parentheses on all calls: debug("I am in subroutine foo", 3);. It's been suggested that prototypes really aren't a good idea. TMTOWTDI – Keith Thompson 3 hours

Except I'll tend to do:

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

which can be less clear when reading, and can be a pain to type. Whenever you double up parenthese, you're asking for trouble. The last thing I want to do is debug my debug statements.

Why do you want prototypes? See this question How to pass optional parameters to a Perl function – TLP

Yes, there are lots of problems with prototyping. The main problem is that it simply doesn't do what people think it should do: Declare the variable types for the parameters you're passing to your function.

This is not the reason I'm using prototyping here.

I rarely use prototypes. In fact, this is probably the only case in all of my code where I do.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

鹿港巷口少年归 2024-12-22 15:52:15

完全摆脱原型:

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
}

Just get rid of prototypes altogether:

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
}
林空鹿饮溪 2024-12-22 15:52:15

原型附加到 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“debug 是一个可以接受一两个参数的函数,每个参数都在调用站点上施加了标量上下文”。

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

my @array = qw(one two);

debug @array;

那么 @array 会在标量上下文中看到,并变成 2。因此,调用变为 debug 2;,而不是您可能期望的 debug 'one', 'two';

The prototype is attached to the coderef and not the name, so when you replace the coderef with the new declaration, you are clearing the prototype. You can avoid having to cross-reference and match prototypes with a helper function:

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 is just a wrapper around Scalar::Util's set_prototype function, which allows you to change the prototype of a coderef after it is created.

Prototypes can be very useful, but when using the scalar prototype, always ask yourself if that is really what you intended. Because the ($;$) prototype tells perl "debug is a function that can take one or two arguments, each with scalar context imposed on the call site".

The bit about context is where people usually get tripped up, because then if you try to do this:

my @array = qw(one two);

debug @array;

Then @array gets seen in scalar context, and becomes 2. So the call becomes debug 2; rather than debug 'one', 'two'; as you might have expected.

梦里兽 2024-12-22 15:52:15

如果我理解正确,您需要原型化并预先声明,以便您可以在同一文件中使用该函数(原型化和无括号)。这就是 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";
}

If I am understanding correctly, you want to prototype and predeclare so that you can use the function (prototyped and braceless) within the same file. This is what the subs pragma is for.

For example, this code works correctly:

#!/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";
}
左耳近心 2024-12-22 15:52:15

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

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

... meat of the program ...

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

You have to declare the same prototype when you define your subroutine:

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

... meat of the program ...

sub debug($;$) {
    ...
}
倦话 2024-12-22 15:52:15

操作方法如下:

sub debug;  #Prototype debug subroutine

#Here goes the main program code/

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

Here's how to do it:

sub debug;  #Prototype debug subroutine

#Here goes the main program code/

sub debug($;$) {
   #Here goes the debug subroutine code/
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文