我正在尝试使用接收子例程并重新定义子例程的方法创建模块。我在主脚本中重新定义子例程没有问题,但是在方法中似乎没有相同的语法:
use strict;
use warnings;
use ReDef;
sub orig{
print "Original!\n";
}
orig;
*orig=sub{print "not Original!\n";};
orig;
ReDef::redef(\&orig);
orig;
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();
正确的语法是什么?
答案 0 :(得分:6)
您的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;
答案 1 :(得分:3)
这对我有用:
use v5.16;
use strict;
use warnings;
package Redef;
sub redef {
my $ref = shift;
${$ref} = sub { say "Redefined!"; }
}
package main;
my $orig = sub { say "Original!"; };
Redef::redef(\$orig);
$orig->(); # Redefined!
虽然这只是反复试验的结果,但我很乐意看到更好的答案。
可能让你困惑的是typeglob运算符*
。在Perl中,使用sigil(${$scalar_ref}
,@{$array_ref}
)和*
运算符取消引用用于符号表技巧 - 也可以在您的情况下使用,请参阅@tobyink的答案