为什么这个Perl产生"不是CODE参考?"

Kin*_*ong 5 perl symbol-tables subroutine typeglob

我需要在运行时从Perl符号表中删除一个方法.我尝试使用undef &Square::area,这会删除函数,但会留下一些痕迹.具体来说,当$square->area()被调用时,Perl抱怨它是"不是CODE引用"而不是"Undefined subroutine&Square :: area called"这是我所期望的.

你可能会问,"为什么这很重要?你删除了这个功能,你为什么要打电话呢?" 答案是,我不是在呼唤它,Perl是.广场由矩形继承,我想继承链传递$square->area通过到&Rectangle::area,但而不是跳过广场,在那里该方法不存在,再通过下降到矩形的面积()方法调用与死"不是CODE参考."

奇怪的是,这似乎只发生在&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 $@;
Run Code Online (Sandbox Code Playgroud)

更新:我已经使用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,我会很感激.

Eth*_*her 7

操纵符号表引用自己必然会让你陷入困境,因为有很多很难找到的小东西.幸运的是,有一个模块可以为你完成所有繁重的工作,Package :: Stash - 所以只需调用它的方法add_package_symbolremove_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::"});
Run Code Online (Sandbox Code Playgroud)

打印(删节):

    $VAR1 = {
              'howdy' => *::howdy,
              'foo' => *::foo,
    };

正如你所看到的,'howdy'插槽仍然存在 - 未定义&howdy实际上并没有做足够的事情.你需要显式删除glob插槽*howdy.