我正在尝试使用接收子例程并重新定义它的方法来创建模块。我在主脚本中重新定义子例程没有问题,但是相同的语法在该方法中似乎不起作用:

main.pl

use strict;
use warnings;
use ReDef;

sub orig{
    print "Original!\n";
}
orig;
*orig=sub{print "not Original!\n";};
orig;
ReDef::redef(\&orig);
orig;


ReDef.pm

package ReDef;

use strict;
use warnings;

sub redef {
    my $ref=shift;
    *ref = sub {print "Redefined!";}
}

1;


测试输出:

perl main.pl
Original!
Subroutine main::orig redefined at main.pl line 9.
not Original!
not Original!


ReDef :: redef()不重新定义。我的看法是,* ref是一个coderef,并为其分配另一个子例程应该更改main :: orig();。

正确的语法是什么?

最佳答案

您的redef函数应如下所示:

package ReDef;
use strict;
use warnings;
sub redef {
   my $ref = shift;
   no warnings qw(redefine);
   *$ref = sub { print "Redefined!" };
}


而且您不应该这样称呼它:

ReDef::redef(\&orig);


相反,您必须这样称呼它:

ReDef::redef(\*orig);


为什么?调用orig时,您正在通过符号表查找名称“ orig”,因此redef函数需要更改符号表,以便它可以将该名称指向另一段代码。 Globrefs基本上是指向符号表的一点的指针,因此这就是您需要传递给ReDef::redef的内容。

打个比方,想像一下,当您想知道刘易斯战役的日期时,您的程序是去图书馆,在目录中寻找13世纪英国战役的书架地址,然后转到该书架,并查找日期...瞧1264年5月14日!现在,假设我想向您提供更改后的信息。仅仅定义一个新的coderef就像在架子上放一本新书:它不会欺骗您,因为目录仍然指向您旧书。我们也需要更改目录。

更新

您可以使用原型使它更漂亮。通常不建议使用原型,但这似乎是对它们的非邪恶用途。

use strict;
use warnings;

sub ReDef::redef (*) {
   my $ref = shift;
   no warnings qw(redefine);
   *$ref = sub { print "Redefined!\n" };
}

sub orig { print "Original!\n" }
orig;

ReDef::redef *orig;  # don't need the backslash any more
orig;

10-07 15:05