Использование констант в Perl

я пытаюсь определить константы в Perl, используя constant ПРАГМА:

use constant {
    FOO => "bar",
    BAR => "foo"
};

у меня небольшие неприятности, и я надеюсь, что есть стандартный способ справиться с этим.

прежде всего...

я определяю сценарий крючка для Subversion. Чтобы сделать вещи простыми, я хочу иметь один файл, где класс (пакет), который я использую, находится в том же файле, что и мой фактический скрипт.

большая часть этого пакета будет иметь константы, участвующие в это:

 print "This is my program";

 package MyClass;

 use constant {
    FOO => "bar"
 };

 sub new { ... }

я хотел бы мой постоянный FOO чтобы быть доступным для моей основной программы. Я хотел бы сделать это, не обращаясь к нему как MyClass::FOO. Обычно, когда пакет представляет собой отдельный файл, я могу сделать это в своей основной программе:

use MyClass qw(FOO);

но, поскольку мой класс и программа представляют собой один файл, я не могу этого сделать. Каким будет лучший способ для моей основной программы получить доступ к моим константам, определенным в моем классе?

второй вопрос...

я хотел бы использовать постоянные значения в качестве хэш-ключей:

$myHash{FOO} = "bar";

проблема в том, что %myHash имеет строковый литерал FOO как ключ, а не значение константы. Это вызывает проблемы, когда я делаю такие вещи:

if (defined $myHash{FOO}) {
   print "Key " . FOO . " does exist!n";
}

я мог бы заставить контекст:

if (defined $myHash{"" . FOO . ""}) {

я мог бы добавить скобки:

if (defined $myHash{FOO()}) {

или я мог бы использовать временную переменную:

my $foo = FOO;
if (defined $myHash{$foo}) {

ни один из них на самом деле хорошие способы решения этой проблемы. Итак, каков наилучший способ? Есть ли хоть один способ, по которому я скучаю?

кстати, я не хочу использовать Readonly::Scalar потому что это 1). медленно и 2). не входит в стандартный пакет Perl. Я хочу определить свой крюк, чтобы не требовать дополнительных пакетов Perl и быть максимально простым в работе.

4 ответов


если вы хотите сохранить все в одном файле, вы можете определить свой пакет констант следующим образом:

use warnings;
use strict;

BEGIN {  # BEGIN means this will all happen at compile time
    package Constants;

    $INC{'Constants.pm'}++;     # tell `require` that the package is loaded
    use base 'Exporter';        # setup package to export
    our @EXPORT_OK = qw( PI );  # what to export

    use constant PI => 3.14159; # define your constant
}

package main;

use Constants qw( PI );  # use it like normal

print PI;

затем, чтобы обмануть автоматическое цитирование внутри хэш-индексов, вы можете написать это так:$hash{+PI} или $hash{(PI)} или $hash{PI()} или $hash{&PI} или $hash{::PI} ... Я, наверное, мог бы продолжать, но думаю, ты понял.

причина $INC{'Constants.pm'}++ необходимо, потому что use Constants qw( PI ); линия на самом деле означает:

BEGIN {
    require 'Constants.pm';
    Constants->import( qw( PI ) );
}

и require проверить %INC чтобы увидеть, был ли пакет уже загружен. Поэтому, давая ему истинное значение (1 в этом случае),require 'Constants.pm'; часть use станет no-op.


  1. на Perl константы не являются константами. Они определяются компиляцией как особый вид функции, которая встроена во время компиляции.

  2. Я нахожу, что Perl "константы" больше боль, чтобы использовать, чем не использовать. Итак, обычно мой подход заключается в использовании скаляров со всеми прописными буквами. my $PI = 3.14159;.


Barewords автоматически цитируются, когда они появляются в хэш-поиске. Вам нужно заставить sub, который реализует константу, которая будет вызываться вместо этого:

$myHash{FOO}   = 'bar'; # doesn't work, bareword quoted
$myHash{+FOO}  = 'bar'; # okay
$myHash{&FOO}  = 'bar'; # okay
$myHash{FOO()} = 'bar'; # okay

экспорт функций и переменных из одного пакета в другой-это все манипуляции с таблицей символов. The Exporter модуль делает это легким для нас, но это не слишком трудно сделать это без модуля.

package main;
sub stuff { return &FOO x 3 }
*FOO = *MyClass::FOO;
print stuff();               # "barbarbar"

package MyClass;
use constant FOO => "bar";

вы могли бы также сказал

BEGIN { *main::FOO = *FOO }

под package MyClass.


Я второй Эрик Стром ответ.

однако, вот другой способ (но это слишком сильно раскрывает реализацию Perl):

use strict;
use warnings;

package Constants;
sub FOO() { 'bar' }
sub BAR() { 'foo' }
sub main::FOO() { FOO }
sub main::BAR() { BAR }

package main;

print FOO, BAR, "\n";