内容

名称

constant - Perl 编译时常量声明

概要

use constant PI    => 4 * atan2(1, 1);
use constant DEBUG => 0;

print "Pi equals ", PI, "...\n" if DEBUG;

use constant {
    SEC   => 0,
    MIN   => 1,
    HOUR  => 2,
    MDAY  => 3,
    MON   => 4,
    YEAR  => 5,
    WDAY  => 6,
    YDAY  => 7,
    ISDST => 8,
};

use constant WEEKDAYS => qw(
    Sunday Monday Tuesday Wednesday Thursday Friday Saturday
);

print "Today is ", (WEEKDAYS)[ (localtime)[WDAY] ], ".\n";

描述

此编译时指令允许您在编译时声明常量。

当您使用上面显示的方法声明一个常量,例如 PI 时,您的脚本运行的每台机器都可以使用尽可能多的精度。此外,您的程序将更容易阅读,更有可能被维护(并且正确维护),并且不太可能因为您在某个方程式中写了 3.14195 而导致太空探测器飞往错误的星球。

当常量在表达式中使用时,Perl 会在编译时用其值替换它,然后可以进一步优化表达式。特别是,如果常量为假,则 if (CONSTANT) 块中的任何代码都将被优化掉。

注意

与所有 use 指令一样,定义常量发生在编译时。因此,将常量声明放在条件语句中(如 if ($foo) { use constant ... })可能不正确。

使用此模块定义的常量不能像变量一样插入字符串中。但是,连接可以正常工作。

print "Pi equals PI...\n";        # WRONG: does not expand "PI"
print "Pi equals ".PI."...\n";    # right

即使引用可能被声明为常量,引用可能指向可以更改的数据,如下面的代码所示。

use constant ARRAY => [ 1,2,3,4 ];
print ARRAY->[1];
ARRAY->[1] = " be changed";
print ARRAY->[1];

常量属于它们定义所在的包。要引用另一个包中定义的常量,请指定完整的包名,例如 Some::Package::CONSTANT。常量可以由模块导出,也可以作为类方法或实例方法调用,即 Some::Package->CONSTANT$obj->CONSTANT,其中 $objSome::Package 的实例。子类可以定义自己的常量来覆盖其基类中的常量。

从本模块的 1.32 版本开始,常量可以在除调用者之外的包中定义,方法是在常量名称中包含包名。

use constant "OtherPackage::FWIBBLE" => 7865;
constant->import("Other::FWOBBLE",$value); # dynamically at run time

使用全大写字母作为常量名称仅仅是一种约定,尽管建议这样做,以便使常量脱颖而出并帮助避免与其他裸字、关键字和子例程名称发生冲突。常量名称必须以字母或下划线开头。以双下划线开头的名称是保留的。一些糟糕的名称选择会在编译时启用警告的情况下生成警告。

列表常量

常量可以是包含一个以上(或以下)值的列表。没有值的常量在标量上下文中评估为 undef。请注意,包含多个值的常量在标量上下文中不会像预期的那样返回其最后一个值。它们目前返回值的个数,但这在将来可能会改变。不要在标量上下文中使用包含多个值的常量。

注意:这意味着定义常量值的表达式在列表上下文中被评估。这可能会产生意外结果。

use constant TIMESTAMP => localtime;                # WRONG!
use constant TIMESTAMP => scalar localtime;         # right

上面的第一行将 TIMESTAMP 定义为一个 9 元素的列表,这是 localtime() 在列表上下文中返回的结果。要将其设置为 localtime() 在标量上下文中返回的字符串,需要显式使用 scalar 关键字。

列表常量是列表,而不是数组。要索引或切片它们,必须将它们放在括号中。

my @workdays = WEEKDAYS[1 .. 5];            # WRONG!
my @workdays = (WEEKDAYS)[1 .. 5];          # right

一次定义多个常量

您可以通过提供一个哈希的引用来定义单个语句中的多个常量,而不是常量名称,其中键是将要定义的常量的名称,而不是编写多个 use constant 语句。显然,使用这种方法定义的所有常量都必须具有单个值。

use constant {
    FOO => "A single value",
    BAR => "This", "won't", "work!",        # Error!
};

这是 Perl 中构建哈希方式的根本限制。当这种情况发生时产生的错误消息通常非常神秘——在最坏的情况下,可能根本没有错误消息,你只会后来发现某些东西坏了。

在定义多个常量时,您不能使用在同一声明中定义的其他常量的值。这是因为调用包在 use 语句完成之后才知道该组中的任何常量。

use constant {
    BITMASK => 0xAFBAEBA8,
    NEGMASK => ~BITMASK,                    # Error!
};

魔术常量

可以在编译时将神奇的值和引用转换为常量,从而允许进行像这样的很酷的事情。(这些错误号并不完全可移植,唉。)

use constant E2BIG => ($! = 7);
print   E2BIG, "\n";        # something like "Arg list too long"
print 0+E2BIG, "\n";        # "7"

您不能通过提供一个绑定标量作为值来生成一个绑定的常量。但是,对绑定变量的引用可以用作常量,没有任何问题。

技术说明

在当前实现中,标量常量实际上是可内联的子例程。从 Perl 5.004 版本开始,适当的标量常量直接插入到一些子例程调用的位置,从而节省了子例程调用的开销。有关此过程的详细信息,请参阅 "perlsub 中的常量函数"

在极少数情况下,您需要在运行时发现某个特定常量是否已通过此模块声明,您可以使用此函数检查哈希 %constant::declared。如果给定的常量名称不包含包名,则使用当前包。

sub declared ($) {
    use constant 1.01;              # don't omit this!
    my $name = shift;
    $name =~ s/^::/main::/;
    my $pkg = caller;
    my $full_name = $name =~ /::/ ? $name : "${pkg}::$name";
    $constant::declared{$full_name};
}

注意事项

列表常量不会内联,除非您使用的是 Perl v5.20 或更高版本。在 v5.20 或更高版本中,它们仍然不是只读的,但这可能会在将来的版本中发生变化。

在同一个包中,不可能有一个子例程或关键字与常量具有相同的名称。这可能是一件好事。

在列表 STDIN STDOUT STDERR ARGV ARGVOUT ENV INC SIG 中具有名称的常量在包 main:: 之外是不允许的,这是出于技术原因。

与某些语言中的常量不同,这些常量不能在命令行或通过环境变量覆盖。

如果您在自动引用裸字的上下文中使用常量(对于任何子例程调用都是如此),您可能会遇到麻烦。例如,您不能说 $hash{CONSTANT},因为 CONSTANT 将被解释为字符串。使用 $hash{CONSTANT()}$hash{+CONSTANT} 来防止裸字引用机制生效。类似地,由于 => 运算符会立即引用其左侧的裸字,因此您必须说 CONSTANT() => 'value'(或者简单地用逗号代替大箭头)而不是 CONSTANT => 'value'

另请参阅

Readonly - 用于创建只读标量、数组、哈希的工具。

Attribute::Constant - 通过属性创建只读变量

Scalar::Readonly - Perl 对 SvREADONLY 标量标志的扩展

Hash::Util - 一组通用的哈希子例程(主要用于锁定/解锁键和值)

错误

请通过 perlbug(1) 实用程序报告任何错误或功能请求。

作者

Tom Phoenix,<[email protected]>,在许多其他人的帮助下。

Casey West,<[email protected]> 添加了同时声明多个常量的功能。

文档主要由 Ilmari Karonen,<[email protected]> 重写。

该程序由 Perl 5 维护者维护。CPAN 发行版由 Sébastien Aperghis-Tramoni <[email protected]> 维护。

版权 & 许可

版权所有 (C) 1997, 1999 Tom Phoenix

本模块是自由软件;您可以根据与 Perl 本身相同的条款重新发布或修改它。