2 Stimmen

Wie lokalisiere ich Variablen in einem anderen Paket, wenn ich ihre Namen nur zur Laufzeit kenne?

Ich muss einige Variablen in einem anderen Package lokalisieren, aber ich werde nicht wissen, wie ihre Namen lauten, bis sie übergeben werden. Meine Versuche, local mit typeglobs zu verwenden, haben nicht funktioniert, also bin ich dazu übergegangen, den Wert der Variable zu speichern und manuell wiederherzustellen. Gibt es einen besseren Weg? Beachten Sie, dass die Fehlerüberprüfung, um zu sehen, ob eine Variable existiert, bevor man damit herumspielt, der Klarheit halber ausgelassen wurde.

#!/usr/bin/perl

use strict;
use warnings;

my %orig;
for my $name (qw/foo bar baz/) {
    my $var = \${$meta::{$name}};
    $orig{$name} = $$var;
    $$var = $$var * 2;
}
meta::p();
for my $name (keys %orig) {
    my $var = \${$meta::{$name}};
    $$var = $orig{$name};
}
meta::p();

package meta;

BEGIN {
    our $foo = 1;
    our $bar = 2;
    our $baz = 3;
}

sub p { print join(" :: ", $meta::foo, $meta::bar, $meta::baz), "\n" }

Ich versuche, ein eval wie dieses zu vermeiden:

my $eval = '';

for my $name (qw/foo bar baz/) {
    $eval .= "local \$meta::$name = \$meta::$name * 2;\n";
}

eval "$eval meta::p()";
meta::p();

Ist es Zeitverschwendung, das eval zu vermeiden? Ist der neue Code schlechter als das eval?

Beachten Sie, dass ich auch keine symbolischen Referenzen verwenden möchte, alle Code-Zeilen müssen unter strict funktionieren. Die aktuelle Lösung funktioniert, daher suche ich nicht nach Hacks, um zu umgehen, was ich tue. Ich suche nach einer besseren Lösung (falls eine existiert).

3voto

daotoad Punkte 26089

Das Ausschalten von strict refs sollte es Ihnen ermöglichen, das zu tun, was Sie mit symbolischen Referenzen möchten. Ich habe derzeit keinen Zugriff auf ein System, um einen Test durchzuführen, aber so etwas sollte funktionieren:

use strict;
use warnings;

{
    no strict `refs`;
    local ${"meta::$name"} = ${"meta::$name"} * 2;

    meta::p();
}

meta::p();

Update:

Nach einigen Tests habe ich einen Knackpunkt gefunden.

Es ist einfach, local auf eine einzelne symbolische Referenz anzuwenden. Aber eine Gruppe von Variablennamen zu lokalisieren, ist nicht so einfach - die Schleifenkonstrukte (map, for usw.) setzen alle einen kleinen Bereich um den Ausdruck, auf den sie angewendet werden, was die Lokalisierung beendet.

# Dies funktioniert, aber funktioniert nicht bei einem Array von Namen.
{   no strict 'refs';

    local ( ${'meta::foo'}, ${'meta::bar'}, ${'meta::baz'} );
    meta::p();
}
meta::p();

# DAS FUNKTIONIERT GAR NICHT!
{   no strict 'refs';

    my @to_localize = map "meta::$_", qw/foo bar baz/;

    local ${$_} = $$_ * 2 for @to_localize;
    meta::p();

}
meta::p();

Die einzige Lösung, die ich finden konnte, verwendet goto, was wir alle wissen, ist als schädlich angesehen.

{   no strict 'refs';

    my @to_localize = map "meta::$_", qw/foo bar baz/;

    LOCALIZER:
        my $localize_me = shift @to_localize;
        local ${$localize_me} = $$localize_me * 2;
        goto LOCALIZER if @to_localize;

    meta::p();

}
meta::p();

Ich bin offen für bessere Ideen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X