Wie greife ich auf eine Konstante in Perl, dessen Name in einer Variablen enthalten ist?

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

  •  25-09-2019
  •  | 
  •  

Frage

Ich habe eine Reihe von Konstanten in Perl erklärt:

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

Wie konstruiere ich einen Perl-Ausdruck, der auf $which_constant basierte, leitet den Wert einer Konstante mit dem Wert dieser Variablen mit dem Namen - zum Beispiel "222".

Bitte beachten Sie, dass ich eine der oben genannten Bedingungen nicht ändern kann - sie sind eine Vereinfachung eines realen Szenarios: Ich habe ein Modul (die ich habe keine Kontrolle über), aus denen diese Konstanten importiert werden. Der `Name eines des Konstanten wird durch den Benutzer von der Befehlszeile geliefert wird. Ich brauche die entsprechenden Konstanten Wert zuzugreifen.

Ich habe meinen Kopf gegen die Wand zu schlagen ist (meist um alle möglichen seltsamen glob-Konstrukte), aber keiner von ihnen zu arbeiten.

P. S. Wenn die Lösung greift auf die Konstanten in ihrem nativen Modul - sagen wir My::Constants::C2 (ohne sie zu importieren), noch besser, aber nicht notwendig - ich die richtigen Konstanten in main:: importieren können leicht My::Constants->import($which_constant) verwenden. und ja, um das Ganze abzurunden, werden te Konstanten nicht standardmäßig so exportierte die explizite Import () -Aufruf zu benötigen.

Einige der Dinge, die ich versucht:

  • main::$which_constant - Syntaxfehler

  • main::${which_constant} - Syntaxfehler

  • ${*$which_constant} - kehrt leer Wert

  • *$which_constant - returns "* main :: C2"

  • ${*${*which_constant}} - leer

War es hilfreich?

Lösung

Konstanten definiert von constant.pm sind nur Subroutinen. Sie können Methodenaufruf Syntax verwenden, wenn Sie den Namen des Konstante in einer Zeichenkette haben:

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

Auf diese Weise, wenn Sie versuchen, eine Konstante zu verwenden, die nicht definiert ist, wird eine Fehlermeldung angezeigt.

Andere Tipps

Perl „Konstanten“ sind eigentlich Subroutinen, die einen konstanten Wert zurück. Der Perl-Compiler ist in der Lage, sie mit dem entsprechenden Wert zum Zeitpunkt der Kompilierung zu ersetzen. Da jedoch der Wert auf einem Runtime-Namen-Suche basiert erhalten möchten, sollten Sie tun:

&{$which_constant}();

(Und natürlich braucht man no strict 'refs' irgendwo.)

Sinan Vorschlag zur Verwendung Methodenaufruf Semantik strict 'refs' Grenzen zu umgehen, ist die sauberste und einfachste Lösung zu lesen.

Meine einzige Sorge darüber war, dass die Geschwindigkeit Strafe für diesen Ansatz könnte ein Problem sein. Wir haben alle über Methodenaufruf Leistungseinbußen und die Geschwindigkeitsvorteile von inlineable Funktionen gehört.

Also habe ich beschlossen, einen Benchmark zu laufen (Code und Ergebnisse folgen).

Die Ergebnisse zeigen, dass normale, inlined Konstanten etwa doppelt so schnell wie Methodenaufrufe mit einer wörtlichen Unterprogrammnamen laufen und fast dreimal so schnell wie Methodenaufrufe mit variabler Subroutinennamen. Der langsamste Ansatz ist ein Standard deref und Aufruf von no strict "refs";.

Aber auch der langsamste Ansatz ist verflixt schnell auf mehr als 1,4 Millionen Mal pro Sekunde auf meinem System.

Dieses Benchmarks auszulöschen meine eine Reservierung über den Methodenaufruf Ansatz, dieses Problem zu lösen.

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

Und die Ergebnisse:

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

Ergebnisse mit ActivePerl 826 unter Windows XP, YMMV erzeugt.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top