2017-02-06 3 views
1

Я хотел бы создать perl-константы из файла конфигурации. Я использую Config :: Файл прочитать конфигурационный файл, который выглядит следующим образом:Определить константы в Perl с именем переменной

ABC = DEF 
GHI = JKL 

С Config :: Файл, который создает hashref, который выглядит следующим образом:

$VAR1 = { 
    'ABC' => 'DEF', 
    'GHI' => 'JKL' 
}; 

Я хотел бы используйте hashref для создания констант, где имя константы должно быть ключом, а значение должно быть соответствующим значением из hashref. Вручную я хотел бы сделать что-то вроде

use constant ABC => 'DEF'; 
use constant GHI => 'JKL'; 

Я пытался сделать что-то это:

foreach my $const (keys %$hashref) { 
    use constant $const => $keys->{$const}; 
} 

, но, как и ожидалось, что не работает. Есть ли способ достичь того, что я пытаюсь сделать?

+5

'использование constants' является Прагма, который вызывается во время компиляции. Ваши переменные существуют только во время выполнения. Вы не можете сделать это за пределами блока «BEGIN» или в eval. Я напишу ответ немного с альтернативами и объяснением. – simbabque

+2

Должен сказать, я никогда не видел большой ценности в «использовании константы» в perl. Кажется, это немного более хрупкое, чем ... ну, просто «нет». – Sobrique

+0

@Sobrique: В моем случае он используется для хранения учетных данных для внешних API. Это самый простой способ сделать их доступными для скриптов, которые в них нуждаются. Какую альтернативу вы бы использовали? – Vince

ответ

5

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

#!/usr/bin/env perl 
use strict; 
use warnings; 
use Data::Dumper; 
use Benchmark qw(:all); 

use constant ABC => 'DEF'; 
use constant GHI => 'JKL'; 

my $ABC = 'DEF'; 
my $GHI = 'JKL'; 

my %test_hash = (
    ABC => 'DEF', 
    GHI => 'JKL' 
); 

sub access_hash { 
    my $value = $test_hash{ABC}; 
    my $value2 = $test_hash{DEF}; 
} 

sub access_constant { 
    my $value = ABC; 
    my $value2 = GHI; 
} 

sub access_scalar { 
    my $value = $ABC; 
    my $value2 = $GHI; 
} 

my $count = 100_000_000; 

cmpthese(
    $count, 
    { 'hash'  => \&access_hash, 
     'constant' => \&access_constant, 
     'scalar' => \&access_scalar 
    } 
); 

Результаты:

   Rate  hash scalar constant 
hash  9427736/s  --  -7%  -10% 
scalar 10143017/s  8%  --  -3% 
constant 10492078/s  11%  3%  -- 

Так что вы правы - это быстрее используйте константу. Тем не менее, я бы предложил для операции, которая работает со скоростью 10M/sec, «сохранение» 5% (или даже 10%) просто не стоит взлома, который вам нужно будет сделать.

Но в интересах собственно ответа на вопрос - корень этой проблемы заключается в том, что constant определяется во время компиляции, где переменные ... нет.

Существует просто отсутствие хеша существующего, когда определено constant, так что это не сработает. Вы также весьма ограничены тем, что вы действительно можете сделать в блоке BEGIN. Я думал бы, что вы могли бы , вероятно, запустить процесс 'compile', чтобы превратить ваш файл конфигурации в .pm, который вы могли бы затем use.

package import_const; 

use constant ABC => 'DEF'; 
use constant GHI => 'JKL'; 

Тогда use import_const; и получить доступ к константы, как import_const::ABC. (Или используйте Exporter, чтобы привести их в локальное пространство имен).

sub from_pkg { 
    my $value = import_const::ABC; 
    my $value2 = import_const::GHI; 
} 

Добавим, что в тест синхронизации:

     Rate  hash  scalar imported constant constant 
hash    9497578/s   --  -6%    -9%  -9% 
scalar   10063399/s   6%   --    -4%  -4% 
imported constant 10473398/s   10%   4%    --  -0% 
constant   10492078/s   10%   4%    0%   -- 

Я думаю, что я до сих пор утверждают, что выгоды являются маргинальными для усилий.Особенно учитывая use constant will surprise you with it's evil

Там может быть модулем, который может делать то, что вам нужно, хотя:

CPAN modules for defining constants

+0

Использование конфигурационного файла в качестве кода и его внедрение в модуль конфигурации будет работать, но это не очень хороший способ сделать это, поскольку побуждение кода из внешнего источника не является хорошей практикой. Спасибо за ответ, однако, мне придется посмотреть на другие способы реализовать то, что мне нужно! – Vince

+1

... да, но введение кода из другого источника - это то, что вы пытаетесь сделать, путем компиляции констант с динамического ввода. – Sobrique

+1

В любом случае, вы можете найти [Review of 'constant' options '] (http://neilb.org/reviews/constants.html), чтобы быть полезным. – Sobrique

4

Прежде всего, вы можете сделать

use constant { 
    CONST1 => VAL1, 
    CONST2 => VAL2, 
    ... 
}; 

use constant LIST 

является эквивалент

BEGIN { 
    require constant; 
    constant->import(LIST); 
} 

или

use constant qw(); 
BEGIN { 
    constant->import(LIST); 
} 

так что вы можете сделать

use constant qw(); 
use FindBin qw($RealBin); 
use JSON::XS qw(decode_json); 

BEGIN { 
    my $qfn = "$RealBin/constants.json"; 
    open(my $fh, '<:raw', $qfn) or die $!; 
    my $file; { local $/; $file = <$fh>; } 
    my $constants = decode_json($file); # { 'ABC' => 'DEF', 'GHI' => 'JKL' }; 
    constant->import($constants); 
} 

Очиститель решение может быть использовать мини-модуль.

package MyConstantsFromFile; 

use strict; 
use warnings; 

use constant qw(); 
use JSON::XS qw(decode_json); 

sub import { 
    my $class = shift; 
    my $qfn = shift; 
    open(my $fh, '<:raw', $qfn) or die $!; 
    my $file; { local $/; $file = <$fh>; } 
    my $constants = decode_json($file); # { 'ABC' => 'DEF', 'GHI' => 'JKL' }; 
    my $import = constant->can('import'); 
    @_ = ('constant', $constants); 
    goto &$import; 
} 

1; 

use FindBin qw($RealBin); 
use MyConstantsFromFile "$RealBin/constants.json";