我目前正在学习有关类型和命名空间的Perls系统。所以我写了一个模块,它接受一个常量的值和名称两个参数,并将常量输出到调用者。 $ package变量等于caller [2]。
*{"$package::$name"} = sub () { return $value; };
上面的代码完成了将匿名子例程导出到调用者符号表中的工作。因为我的目标是构建自己的常量实现,子例程有一个空原型,这意味着它是一个只读子程序。
但这是我的问题:原型不起作用。所以
print &TestConst; #works well
print TestConst(); #works well
print TestConst; #Name "main::TestConst" used only once: possible typo at testscript.pl line 7.
我的想法有什么问题吗?还有另一种方法吗?
答案 0 :(得分:10)
您可以在运行时定义所需的所有符号,但原型只会影响之后编译的代码,因为原型会影响对子调用的解析和编译方式。例如:
use strict;
use warnings;
package Foo;
BEGIN {
*Foo::bar = sub () { 42 };
}
*Foo::baz = sub () { 43 };
my $bar = bar;
my $baz = baz;
print "bar = [$bar], baz = [$baz]\n";
如果我们运行它,它会死于:
在tprot.pl行使用“strict subs”时不允许使用Bareword“baz” 13。
这是由strict
引起的编译 -time错误:编译器看到符号baz
并且不知道它是什么,因为typeglob {{1}直到运行时才会改变。但是*Foo::baz
工作正常,因为它是在bar
块中定义的,它在编译期间立即执行。
IOW,因为裸字是模棱两可的,Perl需要在编译时知道它是sub还是其他东西。因此,您可以在BEGIN
(在隐式import
块中执行)期间安装这些文件,但不能在运行时安装。
此外,原型会影响编译语义;一个常量子程序(如BEGIN
所做的那样)被优化掉了。其他原型导致解析器改变其行为(例如可以接受代码块的subs。)编译器必须在代码中实际遇到对sub的调用之前知道所有这些内容,因此可以正确解析它们。代码在所有内容都已解析后运行。
使用显式parens或带符号调用sub没有此限制,因为Perl在运行时足够聪明,知道这些是子例程调用,并在符号表中动态查找它们。