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

Я пытаюсь определить константы в Perl с помощью constant прагмы:

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

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

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

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

Большая часть этого пакета будет содержать константы:

 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 и как можно проще работать.

Ответы

Ответ 1

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

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.

Ответ 2

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

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

Ответ 3

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

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

Экспорт функций и переменных из одного пакета в другой - все манипуляции с таблицами символов. Модуль 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.

Ответ 4

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

Однако, это другой способ (но это слишком много для реализации 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";