Como faço para acessar uma constante em Perl cujo nome está contido em uma variável?

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

  •  25-09-2019
  •  | 
  •  

Pergunta

Eu tenho um conjunto de constantes declaradas em Perl:

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

Como faço para construir uma expressão perl que, com base em $which_constant, deriva o valor de uma constante nomeada com o valor dessa variável - por exemplo, "222".

Observe que não posso alterar nenhuma das condições acima - elas são uma simplificação de um cenário real: tenho um módulo (sobre o qual não tenho controle) sobre o qual essas constantes são importadas. O `nome de uma das constantes é fornecido pelo usuário na linha de comando. Preciso acessar o valor das constantes apropriadas.

Eu estive batendo na cabeça contra a parede (principalmente em torno de todos os tipos de construções estranhas do Glob), mas nenhum deles funciona.

PS se a solução acessar as constantes dentro de seu módulo nativo - digamos My::Constants::C2 (sem precisar importá -los), melhor, mas não necessário - posso importar as constantes corretas para main:: usando facilmente My::Constants->import($which_constant). E sim, para finalizar, as constantes de TE não são exportadas por padrão, precisando assim da chamada de importação explícita ().

Algumas das coisas que tentei:

  • main::$which_constant - erro de sintaxe

  • main::${which_constant} - erro de sintaxe

  • ${*$which_constant} - retorna valor vazio

  • *$which_constant - Retorna "*main :: c2"

  • ${*${*which_constant}} - vazio

Foi útil?

Solução

Constantes definidas por constant.pm são apenas sub -rotinas. Você pode usar a sintaxe da invocação do método se tiver o nome da constante em uma string:

#!/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 );

Dessa forma, se você tentar usar uma constante que não estiver definida, você receberá um erro.

Outras dicas

As "constantes" Perl são realmente sub -rotinas que retornam um valor constante. O compilador Perl é capaz de substituí -los pelo valor apropriado no momento da compilação. No entanto, como você deseja obter o valor com base em uma pesquisa de nomes de tempo de execução, você deve fazer:

&{$which_constant}();

(E claro que você precisa no strict 'refs' em algum lugar.)

A sugestão de Sinan de usar a semântica da invocação de métodos para se locomover strict 'refs' Os limites são a solução mais limpa e fácil de ler.

Minha única preocupação com isso foi que a penalidade de velocidade para usar essa abordagem pode ser um problema. Todos nós já ouvimos falar sobre as penalidades de desempenho do método e os benefícios de velocidade das funções inlinesáveis.

Por isso, decidi executar um benchmark (código e resultados a seguir).

Os resultados mostram que constantes normais e inlinadas são executadas duas vezes mais rápidas que as chamadas de método com um nome de sub -rotina literal e quase três vezes mais rápido que o método chama com nomes de sub -rotinas variáveis. A abordagem mais lenta é uma deref padrão e invocação de no strict "refs";.

Mas, mesmo a abordagem mais lenta é bastante rápida em mais de 1,4 milhão de vezes um segundo no meu sistema.

Esses benchmarks obliteram minha única reserva sobre o uso da abordagem de chamada de método para resolver esse problema.

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;
}

E os resultados:

                 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%     --

Resultados gerados com o ActivePerl 826 no Windows XP, YMMV.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top