Использование констант в Perl - PullRequest
13 голосов
/ 17 июня 2010

Я пытаюсь определить константы в 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);

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

Вторая проблема ...

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

$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 ]

20 голосов
/ 17 июня 2010

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

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 станет неактивной.

4 голосов
/ 17 июня 2010
  1. Константы Perl не являются константами. Они определены как компиляция, чтобы быть определенным видом функции, которая встроена во время компиляции.

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

3 голосов
/ 17 июня 2010

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

$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.

0 голосов
/ 17 июня 2010

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

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