2017-02-06 28 views
1

我想從配置文件創建perl常量。我使用的配置文件::讀取配置文件看起來像這樣:用變量名在Perl中定義常量

ABC = DEF 
GHI = JKL 

隨着配置::下載,創建一個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 /秒的速度運行,「節省」5%(甚至10%)的操作根本不值得你需要這樣做。

但是爲了實際回答問題 - 這個問題的根源在於constant是在編譯時定義的,其中變量不是。

定義constant時根本沒有散列存在,所以這是行不通的。您在BEGIN區塊中的實際操作能力也非常有限。我的想法是,你可以可能運行一個'編譯'過程來把你的配置文件變成.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

無論如何,你可能會發現[常量'選項'評論](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";