如何以编程方式构造常量名称并使用常量值?

时间:2015-03-05 00:11:27

标签: perl constants

我已经完成了搜索,但无法找到答案。如果在其他地方得到回答,请道歉。

我有一组这样的常量:

use constant {
    STATS_AXLE_SPOT_OK => 0,
    STATS_AXLE_SPOT_ERROR => 1,
    STATS_AXLE_SPOT_SKIPPED => 2,


    STATS_AXLE_FORWARD_OK => 3,
    STATS_AXLE_FORWARD_ERROR => 4,
    STATS_AXLE_FORWARD_SKIPPED => 5,

}

我想做的是有一个函数,我可以构造常量的名称并使用常量的值。

例如

sub DoStuff()
{
  my $l_deal_type = $_[0];
  my $l_status = $_[1];

  #code from here isn't correct.
  my $l_constant_name = "STATS_AXLE_" . $l_deal_type . "_" . $l_status;

  print $l_constant_name;


}

#prints value of constant STATS_AXLE_SPOT_SKIPPED
DoStuff("SPOT", "SKIPPED");

感谢您的帮助!

3 个答案:

答案 0 :(得分:3)

简要总结:首先,不要使用字符串eval。它会损害性能,并且可能会掩盖代码的问题。

其次,主要问题是为什么你需要这样做。如果你需要一个键值查找工具,Perl已经有了一个数据结构,你应该使用它。

如果您只需要在一个地方执行此操作,则可以通过构造其名称来调用使用use constant语句创建的常量作为函数。这就是Hynek -Pichi- Vychodil's answer正在做的事情。

如果您需要在代码中的多个位置执行此操作,最好调整查找,以便您的代码不会出现no strict 'refs'。这就是pilcrow's answer给你的东西。检查常量子例程的定义是否会影响性能,但如果您希望异常告诉您代码的哪一部分试图查找不存在的值,则必须这样做。

对我而言,Const::Fast似乎更适合您的情况。它允许您在不是包命名空间的单个命名空间中收集相关常量,使用简单的Perl结构查找和插入这些常量等。

use Const::Fast;

const my %STATS_AXLE => (
    SPOT_OK => 0,
    SPOT_ERROR => 1,
    SPOT_SKIPPED => 2,
    FORWARD_OK => 3,
    FORWARD_ERROR => 4,
    FORWARD_SKIPPED => 5,
);

然后你可以say $STATS_AXLE{SPOT_ERROR}

say $STATS_AXLE{ "${l_deal_type}_${l_status}" };

say $STATS_AXLE{ "$_[0]_$_[1]" };

是您的DoStuff例程。

如果%STATS_AXLE中不存在该密钥,则此 将会突然显示。

要对CPAN modules for defining constants进行精彩比较,请参阅Neil Bowers' excellent review。我同意他的建议是:

  

如果需要数组或散列常量或不可变的富数据结构,请使用Const::Fast。这与Attribute::Constant之间的竞争非常激烈,但Const::Fast似乎更成熟,而且还有更多   版本。

PS:请注意sub DoStuff()声明DoStuff不接受任何参数。只是不要使用Perl的原型。他们没有做大多数人期望他们做的事情。做:

sub DoStuff {
    ....
}

答案 1 :(得分:2)

Perl的constant有效地定义了子程序,你可以在符号表中检查某个名称的子程序的存在......象征性地:

use strict;
use Carp qw(croak);

....

sub lookup_const {
  my $name = shift;

  croak "No such constant '$name'" unless defined &$name;  # this works even under strict

  no strict 'refs';
  return &{$name};   # this won't work under strict
}

修改

但是,您可能不想这样做。

典型的,简单的标量constant(例如,use constant FLAG_FOO => 1)非常适合定义无限子程序的非常有限的应用程序,这些子程序为开发人员提供了一个有意义的名称,否则"魔术&# 34;文字。这与您可能熟悉的另一种语言中的#define FLAG_FOO 1没有什么不同。

当你超越这个简单的用法时,constant的实现将很快发生。按名称的动态常量查找会破坏常量subs的内联。插值"常数"使用只读(按惯例或其他方式)变量更容易。等

看看other suggestions

答案 2 :(得分:1)

如果你看constant.pm,你会发现常量只是一个模块,它安装一个函数,将常量值返回到当前命名空间。它在编译时完成,然后Perl在下面的代码中将其优化为常量。关键是仍然有功能,所以你可以调用它。

sub DoStuff
{
  my $l_deal_type = $_[0];
  my $l_status = $_[1];

  my $l_constant_name = "STATS_AXLE_${l_deal_type}_${l_status}";

  no strict 'refs';    
  print $l_constant_name->();
}

如果你绝望,可以将constantConst::Fast结合起来并制作这些奇怪的内容:

use strict;
use warnings;
use Const::Fast;
const my %STATS_AXLE => (
    SPOT_OK => 0,
    SPOT_ERROR => 1,
    SPOT_SKIPPED => 2,
    FORWARD_OK => 3,
    FORWARD_ERROR => 4,
    FORWARD_SKIPPED => 5,
);
use constant STATS_AXLE => \%STATS_AXLE;

use v5.10;
for my $type (qw(SPOT FORWARD)) {
  for my $status (qw(OK ERROR SKIPPED)) {
    say "1st way STATS_AXLE_${type}_$status => ", $STATS_AXLE{"${type}_$status"};
    say "2nd way STATS_AXLE_${type}_$status => ", STATS_AXLE->{"${type}_$status"}; 
  }
}
# this works as well
say $STATS_AXLE{FORWARD_ERROR};
say STATS_AXLE->{FORWARD_ERROR};