2010-02-02 101 views
4

我有一组在Perl声明的常量的:如何访问名称包含在变量中的Perl常量?

use constant C1 => 111; 
    use constant C2 => 222; 
    .. 
    use constant C9 => 999; 
    my $which_constant = "C2"; 

如何构建,一个Perl表达式,其基于$which_constant,得出与此变量的值命名为恒定值 - 例如“222”。

请注意,我不能改变上述任何条件 - 它们是一个真实场景的简化:我有一个模块(我无法控制)从中导入这些常量。其中一个常量的名称由用户从命令行提供。我需要访问适当的常量值。

我一直在殴打我的头靠在墙上(主要是围绕各种奇怪的glob结构),但没有一个能够工作。

P.S.如果解决方案访问其本地模块内的常量 - 例如My::Constants::C2(无需导入它们),甚至更好,但不是必需的 - 我可以使用My::Constants->import($which_constant)轻松地将正确的常量导入main::。是的,为了实现它,常量不会默认导出,因此需要显式import()调用。

一些我尝试过的事情:

  • main::$which_constant - 语法错误

  • main::${which_constant} - 语法错误

  • ${*$which_constant} - 返回空值

  • *$which_constant - 返回“ * main :: C2“

  • ${*${*which_constant}} - 空

+1

如果你有机会改变这一模块,http://search.cpan.org/perldoc?Readonly或http://search.cpan.org/perldoc?Attribute::Constant似乎为此使用更适合。 – ephemient 2010-02-02 22:01:02

+1

软件工程团队正在控制模块(对我来说更精确,是自动生成的包含常量列表的数据库后端)。他们没有带宽容量来打扰这种微小/琐碎的事情,并且不会让团队以外的人混淆他们的代码,而没有主要业务需要证明变更风险的合理性。他们是在大型金融机构工作时的休息时间。 – DVK 2010-02-03 18:29:29

回答

13

常量只是子程序。您可以使用方法调用的语法,如果你在一个字符串有固定的名称:

#!/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); 

这样一来,如果您尝试使用不定义一个常数,你会得到一个错误。

+0

You'da !男人思南 – DVK 2010-02-02 21:09:49

+0

这是公认的答案一个可能的候选人,除非有人想出一些甚至是狡猾的(不可能的,但,这是Perl中我们所谈论的:)) – DVK 2010-02-02 21:10:40

+0

@DVK谢谢;-) – 2010-02-02 21:12:11

8

Perl的 “常量” 实际上是返回一个恒定值的子程序。 perl编译器能够在编译时用适当的值替换它们。不过,既然你想根据运行名称查找值,你应该做的:

&{$which_constant}(); 

(当然,你需要no strict 'refs'地方。)通过constant.pm定义

+5

或'$ which_constant - >()'或者只是'&$ which_constant' – mob 2010-02-02 21:21:47

+0

花括号是不必要的。 '&$ which_constant()'工作,如果你想跳过魔术'@ _',处理'&$ which_constant'(不是那么重要)。 – ephemient 2010-02-02 22:06:42

+0

JS - +1提醒我有关“常量只是子程序”,它跳过了我的思路(你的回答是在思南之前,所以即使他的解决方案更符合我的喜好并因此被“接受”也是当之无愧的。 – DVK 2010-02-03 18:31:37

4

思南建议使用方法调用语义来避开strict 'refs'限制是最简洁,最容易阅读的解决方案。

我唯一担心的是使用这种方法的速度惩罚可能是一个问题。我们都听说过方法调用性能惩罚和可嵌入函数的速度优势。

所以我决定运行一个基准(代码和结果如下)。

结果表明,正常,内联常量运行约快两倍,法文字子程序名字叫,几乎三次一样快,方法与变量的子程序调用的名称。最慢的方法是标准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; 
} 

而且结果:在Windows XP中,情况因人而异与826的activeperl产生

    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%  -- 

结果。

+0

对于我目前的任务是一个无关紧要的问题(一次性配置调用),但总的来说,这是一个值得关注的问题。我已经在Perl项目中实现了主要的减速,通过重新设计来解决这个问题。 – DVK 2010-02-03 20:02:53