Comment puis-je accéder à une constante en Perl dont le nom est contenu dans une variable?

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

  •  25-09-2019
  •  | 
  •  

Question

J'ai un ensemble de constantes déclarées en Perl:

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

Comment puis-je construire une expression Perl qui, sur la base $which_constant, tire la valeur d'une constante appelée avec la valeur de cette variable - par exemple "222".

S'il vous plaît noter que je ne peux pas modifier les conditions ci-dessus - ils sont une simplification d'un scénario réel: J'ai un module (que je ne contrôle pas) à partir de laquelle ces constantes sont importées. Le `nom de l'une des constantes est fournie par l'utilisateur à partir de la ligne de commande. J'ai besoin d'accéder à la valeur des constantes appropriées.

J'ai battais ma tête contre le mur (la plupart du temps autour de toutes sortes de constructions étranges glob), mais aucun d'entre eux travaillent.

P.S. Si la solution accède aux constantes à l'intérieur de leur module natif - dire My::Constants::C2 (sans avoir besoin de les importer), encore mieux, mais pas nécessaire - je peux importer les constantes correctes dans main:: facilement à l'aide My::Constants->import($which_constant). et oui, pour couronner le tout, te constantes ne sont pas exportés par défaut ont donc besoin de l'appel import () explicite.

Certaines des choses que j'essayées:

  • main::$which_constant - erreur de syntaxe

  • main::${which_constant} - erreur de syntaxe

  • ${*$which_constant} - Retourne la valeur vide

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

  • ${*${*which_constant}} - vide

Était-ce utile?

La solution

Constantes définies par constant.pm sont juste subroutines. Vous pouvez utiliser la syntaxe d'appel de méthode si vous avez le nom de la constante dans une chaîne:

#!/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 cette façon, si vous essayez d'utiliser une constante qui n'est pas définie, vous obtiendrez une erreur.

Autres conseils

Perl « constantes » sont en fait des sous-routines qui renvoient une valeur constante. Le compilateur Perl est capable de les remplacer par la valeur appropriée au moment de la compilation. Cependant, puisque vous voulez obtenir la valeur basée sur une recherche de nom d'exécution, vous devez faire:

&{$which_constant}();

(Et bien sûr, vous avez besoin no strict 'refs' quelque part.)

suggestion de Sinan d'utiliser la sémantique d'invocation de méthode pour contourner les limites de strict 'refs' est la plus propre, plus facile à lire solution.

Ma seule préoccupation à ce sujet est que la peine de vitesse pour l'utilisation de cette approche pourrait être un problème. Nous avons tous entendu parler des pénalités de performance d'appel de la méthode et les avantages de la vitesse des fonctions inlineable.

Alors j'ai décidé de lancer une référence (code et les résultats suivent).

Les résultats montrent que les constantes normales, inline courent deux fois plus vite que la méthode appelle avec un nom de sous-programme littéral, et près de trois fois plus vite que les appels de méthode avec les noms de sous-programme variable. La plus lente approche est un deref standard et invocation de no strict "refs";.

Mais, même la plus lente approche est sacrément rapide à plus de 1,4 million de fois par seconde sur mon système.

Ces repères effacent ma une réserve sur l'utilisation de l'approche d'appel de méthode pour résoudre ce problème.

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

Et les résultats:

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

Les résultats générés avec ActivePerl 826 sous Windows XP, YMMV.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top