2010-02-02 3 views
4

У меня есть набор констант, объявленных в Perl:Как получить доступ к константе в Perl, чье имя содержится в переменной?

use constant C1 => 111; 
    use constant C2 => 222; 
    .. 
    use constant C9 => 999; 
    my $which_constant = "C2"; 

Как построить выражение Perl, которое, основываясь на $which_constant, выводит значение константы с именем, значением этой переменной - например, "222".

Обратите внимание: я не могу изменить ни одно из приведенных выше условий - это упрощение реального сценария: у меня есть модуль (который я не контролирую), из которого импортируются эти константы. Имя одной из констант предоставляется пользователем из командной строки. Мне нужно получить доступ к значению соответствующих констант.

Я бил головой о стену (в основном вокруг всякого рода странных конструкций глобуса), но никто из них не работает.

P.S. Если решение обращается к константам внутри своего собственного модуля - скажем My::Constants::C2 (без необходимости их импорта), еще лучше, но не обязательно - я могу импортировать правильные константы в main:: с помощью My::Constants->import($which_constant). и да, чтобы завершить это, т. е. константы НЕ экспортируются по умолчанию, тем самым требуя явного вызова import().

Некоторые из вещей, которые я пробовал:

  • main::$which_constant - ошибка синтаксиса

  • main::${which_constant} - синтаксические ошибки

  • ${*$which_constant} - возвращает пустое значение

  • *$which_constant - возвращает " * main :: C2 "

  • ${*${*which_constant}} - пустая

+1

Если вам когда-либо понадобится изменить этот модуль, http://search.cpan.org/perldoc?Readonly или http://search.cpan.org/perldoc?Attribute::Constant, похоже, намного лучше подходит для этого использования. – ephemient

+1

Модуль находится под контролем (для меня более точным, автоматически создается вне базы данных БД, содержащей список констант) команды разработчиков программного обеспечения. У них нет пропускной способности, чтобы беспокоиться о чем-то маленьком/тривиальном, и не позволят кому-то извне работать с кодом без серьезного бизнеса, чтобы оправдать риск изменения. Их перерывы при работе в крупном финансовом учреждении. – DVK

ответ

13

Константы, определенные constant.pm просто подпрограммы. Вы можете использовать метод вызова синтаксис, если у вас есть имя константы в строке:

#!/usr/bin/perl -l 

use strict; use warnings; 
use constant C1 => 111; 
use constant C2 => 222; 

print __PACKAGE__->$_ for qw(C1 C2); 
# or print main->$_ for qw(C1 C2); 

Таким образом, если вы пытаетесь использовать константу, которая не определена, вы получите сообщение об ошибке.

+0

Вы человек Sinan! – DVK

+0

Это вероятный кандидат на принятый ответ, если кто-то не придумает что-то даже более искусное (маловероятно, но это Perl, о котором мы говорим :)) – DVK

+0

@DVK спасибо ;-) –

8

Perl "константа" на самом деле являются подпрограммами, которые возвращают постоянное значение. Компилятор perl может заменить их соответствующим значением во время компиляции. Однако, так как вы хотите, чтобы получить значение, основанное на имя выполнения поиска, вы должны сделать:

&{$which_constant}(); 

(И, конечно, вам нужно no strict 'refs' где-то.)

+5

или '$ which_constant ->()' или просто '& $ which_constant' – mob

+0

Неверные фигурные скобки. '& $ which_constant()' работает, если вы хотите пропустить магическую '@ _'-обработку, которая' & $ which_constant' (не это важно). – ephemient

+0

JS - +1 для напоминания мне о «константах - это просто подпрограммы», которые пропустили мой разум (ваш ответ был до того, как Синан был настолько заслужен, даже если его решение было по большей части мне по душе и, таким образом, «принято». – DVK

4

Предложение Sinan использовать семантику вызова метода, чтобы обойти ограничения strict 'refs' - это самое чистое и легкое для чтения решение.

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

Итак, я решил запустить бенчмарк (код и результаты следуют).

Результаты показывают, что нормальные, встроенные константы работают примерно в два раза быстрее, чем вызовы методов с именем подпрограммы literal, и почти в три раза быстрее, чем вызовы методов с именами переменных подпрограммы. Самый медленный подход - стандартное разделение и вызов no strict "refs";.

Но даже самый медленный подход довольно прост в скорости более чем 1,4 миллиона раз в секунду в моей системе.

Эти тесты стирают мою одну оговорку об использовании подхода метода вызова для решения этой проблемы.

use strict; 
use warnings; 

use Benchmark qw(cmpthese); 

my $class = 'MyConstant'; 
my $name = 'VALUE'; 
my $full_name = $class.'::'.$name; 


cmpthese(10_000_000, { 
    'Normal'  => \&normal_constant, 
    'Deref'  => \&direct_deref, 
    'Deref_Amp' => \&direct_deref_with_amp, 
    'Lit_P_Lit_N' => \&method_lit_pkg_lit_name, 
    'Lit_P_Var_N' => \&method_lit_pkg_var_name, 
    'Var_P_Lit_N' => \&method_var_pkg_lit_name, 
    'Var_P_Var_N' => \&method_var_pkg_var_name, 
}); 

sub method_lit_pkg_lit_name { 
    return 7 + MyConstant->VALUE; 
} 

sub method_lit_pkg_var_name { 
    return 7 + MyConstant->$name; 
} 

sub method_var_pkg_lit_name { 
    return 7 + $class->VALUE; 
} 

sub method_var_pkg_var_name { 
    return 7 + $class->$name; 
} 

sub direct_deref { 
    no strict 'refs'; 
    return 7 + $full_name->(); 
} 

sub direct_deref_with_amp { 
    no strict 'refs'; 
    return 7 + &$full_name; 
} 

sub normal_constant { 
    return 7 + MyConstant::VALUE(); 
} 

BEGIN { 
    package MyConstant; 

    use constant VALUE => 32; 
} 

И результаты:

    Rate Deref_Amp Deref Var_P_Var_N Lit_P_Var_N Lit_P_Lit_N Var_P_Lit_N Normal 
Deref_Amp 1431639/s  -- -0%   -9%  -10%  -29%  -35% -67% 
Deref  1438435/s  0% --   -9%  -10%  -28%  -35% -67% 
Var_P_Var_N 1572574/s  10% 9%   --   -1%  -22%  -29% -64% 
Lit_P_Var_N 1592103/s  11% 11%   1%   --  -21%  -28% -63% 
Lit_P_Lit_N 2006421/s  40% 39%   28%   26%   --   -9% -54% 
Var_P_Lit_N 2214349/s  55% 54%   41%   39%   10%   -- -49% 
Normal  4353505/s  204% 203%  177%  173%  117%   97%  -- 

Результаты, полученные с ActivePerl 826 на Windows XP, YMMV.

+0

Для моей текущей задачи был нерелевантным (однократный вызов конфигурации), но в действительности это является серьезной проблемой. У меня были MAJOR замедление в проектах Perl, которые были исправлены путем перепроектирования, устраняя вызовы методов в узких циклах после сравнительного сравнения этих двух. – DVK

 Смежные вопросы

  • Нет связанных вопросов^_^