我需要在运行时从Perl符号表中删除一个方法。我尝试使用undef &Square::area
执行此操作,这会删除该功能但会留下一些痕迹。具体来说,当调用$square->area()
时,Perl会抱怨它是“不是CODE引用”而不是“Undefined subroutine& Square :: area called”,这就是我所期望的。
您可能会问,“为什么这很重要?您删除了该功能,为什么要调用它?”答案是,我不是在呼唤它,Perl是。 Square继承自Rectangle,我希望继承链将$square->area
传递给&Rectangle::area
,但不是跳过方法不存在的Square,而是直接转到Rectangle的area(),方法使用“Not a CODE reference”调用die。
奇怪的是,这似乎只发生在& Square :: area由typeglob赋值(例如*area = sub {...}
)定义时。如果使用标准sub area {}
方法定义函数,则代码按预期工作。
同样有趣的是,取消定义整个glob按预期工作。只是不要取消定义子程序本身。
这是一个简短的例子,说明症状,并与正确的行为形成对比:
#!/usr/bin/env perl
use strict;
use warnings;
# This generates "Not a CODE reference". Why?
sub howdy; *howdy = sub { "Howdy!\n" };
undef &howdy;
eval { howdy };
print $@;
# Undefined subroutine &main::hi called (as expected)
sub hi { "Hi!\n" }
undef &hi;
eval { hi };
print $@;
# Undefined subroutine &main::hello called (as expected)
sub hello; *hello = sub { "Hello!\n" };
undef *hello;
eval { hello };
print $@;
更新:我已经使用Package :: Stash解决了这个问题(感谢@Ether),但我仍然对为什么它首先发生的事情感到困惑。 perldoc perlmod
说:
package main;
sub Some_package::foo { ... } # &foo defined in Some_package
这只是编译时typeglob赋值的简写:
BEGIN { *Some_package::foo = sub { ... } }
但似乎它不是只是的简写,因为这两者在取消定义函数后会导致不同的行为。如果有人能告诉我这是否是(1)不正确的文档,(2)perl中的错误,或(3)PEBCAK的情况,我将不胜感激。
答案 0 :(得分:7)
自己操纵符号表引用必然会让你陷入困境,因为有许多令人费解的事情难以理解。幸运的是,有一个模块可以为您完成所有繁重工作,Package::Stash - 所以只需根据需要调用其方法add_package_symbol
和remove_package_symbol
。
您可能希望签出的另一个好的方法安装程序是Sub::Install - 如果您想生成许多类似的功能,那就太好了。
至于为什么你的方法不正确,让我们在删除代码参考后看一下符号表:
sub foo { "foo!\n"}
sub howdy; *howdy = sub { "Howdy!\n" };
undef &howdy;
eval { howdy };
print $@;
use Data::Dumper;
no strict 'refs';
print Dumper(\%{"main::"});
打印(删节):
$VAR1 = { 'howdy' => *::howdy, 'foo' => *::foo, };
正如你所看到的,'howdy'插槽仍然存在 - 取消定义&howdy
实际上并没有任何。您需要显式删除glob插槽*howdy
。
答案 1 :(得分:2)
它发生的原因恰恰是因为你分配了一个typeglob。
当您删除CODE符号时,其余的typeglob仍然会延迟,所以当您尝试执行时它会指向非CODE
的typeglob。