Pregunta

Tengo un conjunto de constantes declaró en Perl:

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

¿Cómo construir una expresión Perl el cual, basado en $which_constant, se deriva el valor de una constante llamada con el valor de esta variable - por ejemplo, "222".

Tenga en cuenta que no puedo cambiar ninguna de las condiciones anteriores - son una simplificación de un escenario real: Tengo un módulo (que no tengo ningún control sobre) a partir del cual se importan estas constantes. El `nombre de una de las constantes se suministra por el usuario desde la línea de comandos. Necesito acceder valor de las constantes apropiadas.

He estado golpeando mi cabeza contra la pared (en su mayoría alrededor de todo tipo de construcciones extrañas glob), pero ninguno de ellos trabajo.

P.S. Si la solución accede a las constantes dentro de su nativa módulo - My::Constants::C2 por ejemplo (sin necesidad de importarlos), aún mejor, pero no es necesario - Me puede importar las constantes correctas en main:: fácilmente usando My::Constants->import($which_constant). y sí, por si fuera poco, te constantes no se exportan de manera predeterminada por lo que requieren la llamada explícita de importación ().

Algunas de las cosas que intenté:

  • main::$which_constant - error de sintaxis

  • main::${which_constant} - error de sintaxis

  • ${*$which_constant} - retornos valor vacío

  • *$which_constant - devuelve "* principal :: C2"

  • ${*${*which_constant}} - vacío

¿Fue útil?

Solución

Las constantes definidas por constant.pm son sólo subrutinas. Puede utilizar la sintaxis de invocación de método si tiene el nombre de la constante en una cadena:

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

De esta manera, si se intenta utilizar una constante que no se define, se obtendrá un error.

Otros consejos

Perl "constantes" en realidad son subrutinas que devuelven un valor constante. El compilador de Perl es capaz de reemplazarlos con el valor adecuado en tiempo de compilación. Sin embargo, ya que desea obtener el valor basado en un nombre de tiempo de ejecución de las operaciones de búsqueda, que debe hacer:

&{$which_constant}();

(Y, por supuesto, usted necesita no strict 'refs' en alguna parte.)

La sugerencia de Sinan a la semántica utilice el método de invocación de moverse límites strict 'refs' es el más limpio, más fácil de leer solución.

Mi única preocupación acerca de esto fue que la penalización de velocidad para el uso de este enfoque podría ser un problema. Todos hemos oído acerca de penalizaciones en el rendimiento de llamadas a métodos y los beneficios de velocidad de funciones inlineable.

Así que decidí correr un punto de referencia (código y los resultados siguen).

Los resultados muestran que, constantes normales inline corren casi el doble de rápido que las llamadas a métodos con un nombre de subrutina literal, y casi tres veces más rápido que las llamadas a métodos con nombres de subrutina variables. El enfoque más lento es un DEREF estándar y la invocación de no strict "refs";.

Sin embargo, incluso el enfoque más lento es bastante maldito rápido en más de 1,4 millones de veces por segundo en mi sistema.

Estos puntos de referencia obliterar mi única reserva sobre el uso del enfoque de llamada a un método para resolver este 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;
}

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

Los resultados son generados con ActivePerl 826 en Windows XP, tu caso es distinto.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top