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

Perl 如何以编程方式构造常量名称并使用常量的值?,perl,constants,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, ST

我已经搜索过了,但找不到这个问题的答案。如果在其他地方得到答复,我会道歉

我有一组常数,如下所示:

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,

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

例如


谢谢你的帮助

简要总结:首先,不要为此使用string
eval
。它会损害性能,并会掩盖代码中的问题

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

如果只需要在一个位置执行此操作,则可以通过构造常量的名称将使用
use constant
语句创建的常量作为函数调用。这就是我们正在做的

如果您需要在代码中的多个位置执行此操作,那么最好通过中介查找,这样您的代码就不会充斥着
没有严格的“ref”
。这就是给你的。检查常量子例程的定义会影响性能,但如果您希望异常告诉您代码的哪个部分试图查找不存在的值,则有必要进行检查

对我来说,这看起来更适合你的情况。它允许您在不是包名称空间的单个名称空间中收集相关常量,使用简单的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,
);
然后你可以做
说$STATS\u axe{SPOT\u ERROR}
或者

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

是您的
DoStuff
例程

如果钥匙不在
%STATS\u车桥中
,则会发出咯咯声

有关的优秀比较,请参见。我同意他的建议,即:

如果需要数组或散列常量,或不可变的富数据结构,请使用。这是一场势均力敌的竞赛,但是
Const::Fast
似乎更成熟,而且已经有了更多的进展 释放

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

sub DoStuff {
    ....
}
Perl有效地定义了子例程,您可以检查符号表中是否存在某个名称的子例程。。。象征性地:

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
}
编辑:

但是,您可能不想这样做

典型的、简单的标量
常量
(例如,
使用常量标志_FOO=>1
)对于定义可内联子例程的非常有限的应用非常有用,这些子例程为开发人员提供了一个有意义的名称,用于其他“神奇”文字。这与您可能熟悉的另一种语言中的
#define FLAG_FOO 1
没有什么不同

当您超越这个简单的用法时,
constant
的实现将很快受到影响。按名称进行动态常量查找会破坏常量子类的内联。使用只读(按惯例或其他方式)变量插值“常量”要容易得多。等等

看一看。

如果你看一看,你会发现常量只是一个模块,它安装了一个将常量值返回到当前名称空间的函数。它在编译时完成,然后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->();
}
如果你感到绝望,你可以结合并制作这种奇怪的东西:

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};

我认为这一切都很好,可以通过使用eval函数来解决。例如,将print$l_constant_name替换为print eval($l_constant_name);尽管如此,还是可以接受关于更好解决方案的建议。使用
eval
获取动态常量听起来是个非常非常糟糕的主意。我相信在99.9999%的时间里,我只会使用正则变量。从未完全理解常量的意义,它是一个不应该更改的变量,实际上与从不更改的变量没有区别。@TLP,有一点是Perl的优化器可以做到,但它不能在变量上做到这一点(即使您设置了一次,并且从不更改它)。然而,常量子例程可以折叠。@cjm只是,一旦试图象征性地调用这些子例程,就会抛出
Constant
可能比
Const::Fast
@cjm更高的性能优势。@cjm啊,是的,我现在记得,从20年前的某个课程开始。:)不过,现在这似乎是一个非常小的优化。我认为,当问题可以用人们已经有的东西解决时,通过安装模块来解决每个问题不是一个好主意。@Hynek Pichi Vychodil OP使用包符号表作为散列。我展示了一种将相似常量分组的方法,这样它们也可以插入,而不必求助于
no strict
。此外,通过这种方式,OP可以通过其值查找
STATS\u轴的名称。引入这样的模块来解决至少20次击键就可以解决的问题是一个非常糟糕的主意。权衡取舍取决于你需要在多少地方键入相同的20次击键。顺便说一句,通过了气味测试:如果没有Debian软件包,你不应该花时间和这个怪人在一起。所以OK:-)@SinanÜnür:一旦您动态访问它,它就不再是内联的,在您的情况下,
$STATS\u axe{“${l_deal\u type}}{u${l_status}}
。那有什么意义呢?您正在抱怨代码中的缺陷。他有一个问题。你建议安装一个模块,现在他有两个问题。我同意他应该使用散列而不是符号表,但你的解决方案让情况变得更糟。我有开发和维护数十万行Perl代码库的经验,这些代码库应该作为一个服务器全天候运行,并且涉及另一个模块来解决一些不到20次键盘笔划就能解决的问题,这总是一个非常糟糕的主意。特别是在模块中
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->();
}
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};