如何在 Perl 中访问名称包含在变量中的常量?
-
25-09-2019 - |
题
我有一组用 Perl 声明的常量:
use constant C1 => 111;
use constant C2 => 222;
..
use constant C9 => 999;
my $which_constant = "C2";
我如何构造一个 Perl 表达式,它基于 $which_constant
, ,导出用该变量的值命名的常量的值 - 例如“222”。
请注意,我无法更改上述任何条件 - 它们是真实场景的简化:我有一个模块(我无法控制),从中导入这些常量。常量之一的名称由用户从命令行提供。我需要访问适当的常量值。
我一直在用头撞墙(主要是围绕各种奇怪的全局结构),但它们都不起作用。
附:如果解决方案访问其本机模块内的常量 - 例如 My::Constants::C2
(不需要导入它们),甚至更好,但不是必需的 - 我可以将正确的常量导入到 main::
轻松使用 My::Constants->import($which_constant)
. 。是的,最重要的是,默认情况下不会导出常量,因此需要显式 import() 调用。
我尝试过的一些事情:
main::$which_constant
- 语法错误main::${which_constant}
- 语法错误${*$which_constant}
- 返回空值*$which_constant
- 返回“*main::C2”${*${*which_constant}}
- 空的
解决方案
常数定义为 constant.pm
只是子程序。如果字符串中有常量的名称,则可以使用方法调用语法:
#!/usr/bin/perl -l
use strict; use warnings;
use constant C1 => 111;
use constant C2 => 222;
print __PACKAGE__->$_ for qw( C1 C2 );
# or print main->$_ for qw( C1 C2 );
这样,如果您尝试使用未定义的常量,您将收到错误。
其他提示
Perl“常量”实际上是返回常量值的子例程。Perl 编译器能够在编译时将它们替换为适当的值。但是,由于您想根据运行时名称查找获取值,因此应该执行以下操作:
&{$which_constant}();
(当然你需要 no strict 'refs'
某处。)
Sinan 建议使用方法调用语义来解决问题 strict 'refs'
limits 是最干净、最容易阅读的解决方案。
我对此唯一担心的是使用这种方法的速度损失可能是一个问题。我们都听说过方法调用性能损失和内联函数的速度优势。
所以我决定运行一个基准测试(代码和结果如下)。
结果表明,正常的内联常量的运行速度大约是使用字面子例程名称的方法调用的两倍,几乎是使用变量子例程名称的方法调用的三倍。最慢的方法是标准的 deref 和调用 no strict "refs";
.
但是,即使是最慢的方法,在我的系统上也非常快,每秒超过 140 万次。
这些基准消除了我对使用方法调用方法来解决此问题的保留意见。
use strict;
use warnings;
use Benchmark qw(cmpthese);
my $class = 'MyConstant';
my $name = 'VALUE';
my $full_name = $class.'::'.$name;
cmpthese( 10_000_000, {
'Normal' => \&normal_constant,
'Deref' => \&direct_deref,
'Deref_Amp' => \&direct_deref_with_amp,
'Lit_P_Lit_N' => \&method_lit_pkg_lit_name,
'Lit_P_Var_N' => \&method_lit_pkg_var_name,
'Var_P_Lit_N' => \&method_var_pkg_lit_name,
'Var_P_Var_N' => \&method_var_pkg_var_name,
});
sub method_lit_pkg_lit_name {
return 7 + MyConstant->VALUE;
}
sub method_lit_pkg_var_name {
return 7 + MyConstant->$name;
}
sub method_var_pkg_lit_name {
return 7 + $class->VALUE;
}
sub method_var_pkg_var_name {
return 7 + $class->$name;
}
sub direct_deref {
no strict 'refs';
return 7 + $full_name->();
}
sub direct_deref_with_amp {
no strict 'refs';
return 7 + &$full_name;
}
sub normal_constant {
return 7 + MyConstant::VALUE();
}
BEGIN {
package MyConstant;
use constant VALUE => 32;
}
结果:
Rate Deref_Amp Deref Var_P_Var_N Lit_P_Var_N Lit_P_Lit_N Var_P_Lit_N Normal
Deref_Amp 1431639/s -- -0% -9% -10% -29% -35% -67%
Deref 1438435/s 0% -- -9% -10% -28% -35% -67%
Var_P_Var_N 1572574/s 10% 9% -- -1% -22% -29% -64%
Lit_P_Var_N 1592103/s 11% 11% 1% -- -21% -28% -63%
Lit_P_Lit_N 2006421/s 40% 39% 28% 26% -- -9% -54%
Var_P_Lit_N 2214349/s 55% 54% 41% 39% 10% -- -49%
Normal 4353505/s 204% 203% 177% 173% 117% 97% --
在 Windows XP 上使用 ActivePerl 826 生成的结果,YMMV。