【问题标题】:How do I access a constant in Perl whose name is contained in a variable?如何在 Perl 中访问名称包含在变量中的常量?
【发布时间】:2011-01-12 08:39:46
【问题描述】:

我在 Perl 中声明了一组常量:

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

我如何构造一个基于$which_constant 的 Perl 表达式,派生一个以该变量的值命名的常量的值 - 例如“222”。

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

我一直在用头撞墙(主要是围绕各种奇怪的 glob 构造),但它们都不起作用。

附:如果解决方案访问其本机模块中的常量——比如My::Constants::C2(不需要导入它们),更好,但不是必需的——我可以使用My::Constants->import($which_constant)轻松地将正确的常量导入main::。是的,最重要的是,默认情况下不会导出 te 常量,因此需要显式的 import() 调用。

我尝试过的一些事情:

  • main::$which_constant - 语法错误

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

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

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

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

【问题讨论】:

  • 如果您要更改此模块,search.cpan.org/perldoc?Readonlysearch.cpan.org/perldoc?Attribute::Constant 似乎更适合这种用法。
  • 模块由软件工程团队控制(更准确地说,是从包含常量列表的数据库后端自动生成)。他们没有带宽容量来处理这么小的/微不足道的事情,并且不会让团队外部的人在没有主要业务需要证明变更风险的情况下弄乱他们的代码。他们是在大型金融机构工作时的休息时间。

标签: perl constants symbol-table


【解决方案1】:

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 :))
  • 是的,使用-> 来暗示子程序评估非常巧妙。绝对比我的方法漂亮。
  • 在这种情况下,您可以说My::Constants->$which_constant 并完全跳过import 步骤。为了更好地检查错误,如果您想确保常量存在,也可以检查 My::Constants->can($which_constant)(尽管这不会区分常量和其他子项。
【解决方案2】:

Perl“常量”实际上是返回常量值的子程序。 perl 编译器能够在编译时用适当的值替换它们。但是,由于您想根据运行时名称查找来获取值,您应该这样做:

&{$which_constant}();

(当然你在某个地方需要no strict 'refs'。)

【讨论】:

  • $which_constant->() 或只是&$which_constant
  • 大括号是不必要的。 &$which_constant() 有效,如果您想跳过魔术@_-处理&$which_constant(没关系)。
  • JS - +1 提醒我“常量只是子程序”这让我没有想到(你的答案在思南之前,所以这是当之无愧的,即使他的解决方案更符合我的喜好,因此“接受”。
【解决方案3】:

Sinan 建议使用方法调用语义来绕过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;
}

结果:

                 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、YMMV 上使用 ActivePerl 826 生成的结果。

【讨论】:

  • 对于我当前的任务,这无关紧要(一次配置调用),但总的来说,这是一个有效的问题。我在 Perl 项目中遇到了严重的减速,在比较两者的基准测试之后,通过重新设计删除紧密循环中的方法调用来纠正这些减速。
猜你喜欢
  • 2011-11-22
  • 2013-08-30
  • 1970-01-01
  • 2013-10-29
  • 1970-01-01
  • 2015-08-17
  • 1970-01-01
  • 2014-10-17
相关资源
最近更新 更多