Как получить доступ к константу в Perl, имя которого содержится в переменной?

StackOverflow https://stackoverflow.com/questions/2187682

  •  25-09-2019
  •  | 
  •  

Вопрос

У меня есть набор констант, заявленных в Perl:

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

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

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

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

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

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

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

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

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

  • *$which_constant - Возвращает "* Главная :: C2"

  • ${*${*which_constant}} - пустой

Это было полезно?

Решение

Константы, определенные 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 );

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

Другие советы

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

&{$which_constant}();

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

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

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

Поэтому я решил запустить ориентир (код и следующие результаты).

Результаты показывают, что нормальные включенные константы запускаются примерно в два раза быстрее, чем метод вызовы с буквальным именем подпрограммы, и почти в три раза быстрее всего, что и вызовы метода с вариабельными именами подсэбина. Самый медленный подход является стандартным дерефом и вызовом 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.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top