Как локализовать переменные в другом пакете, когда я знаю их имена только во время выполнения? - PullRequest
2 голосов
/ 27 апреля 2009

Мне нужно локализовать некоторые переменные в другом пакете, но я не буду знать, как их имена, пока они не будут переданы. Мои попытки использовать local с typeglobs не сработали, поэтому я вернулся к сохранению значение переменной и восстановление ее вручную. Есть ли способ лучше? Обратите внимание, что проверка на наличие ошибок, чтобы увидеть, существует ли переменная перед тем, как с ней связываться, для ясности исключена.

#!/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" }

Я пытаюсь избежать eval, как это:

my $eval = '';

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

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

Является ли попытка избежать оценки бесполезной тратой времени? Новый код хуже, чем eval?

Обратите внимание, я также не хочу использовать символические ссылки, весь код должен работать в strict. Текущее решение работает, поэтому я не ищу хаков, чтобы обойти то, что я делаю, я ищу лучшее решение (если оно существует).

1 Ответ

3 голосов
/ 27 апреля 2009

Отключение strict refs должно позволить вам делать то, что вы хотите с символьными ссылками . У меня нет доступа к системе для тестирования с банкоматом, но что-то вроде этого должно работать:

use strict;
use warnings;

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

    meta::p();
}

meta::p();

Обновление:

После некоторых испытаний я обнаружил загвоздку.

В то время как local легко использовать для одной символической ссылки. Но локализовать массив имен переменных не так просто - конструкции цикла (map, for и т. Д.) Создают небольшую область видимости вокруг выражения, с которым они работают, что завершает локализацию.

# This works but does not work on an array of names.
{   no strict 'refs';

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


# THIS DOES NOT WORK AT ALL!
{   no strict 'refs';

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

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

}
meta::p();

Единственное решение, которое я смог найти, использует goto, который, как мы все знаем, считается вредным .

{   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();

Я открыт для лучших идей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...