Попытка локализовать внешнюю переменную пакета с помощью лексической привязки в Perl - PullRequest
4 голосов
/ 18 октября 2011

Это длинное название, но, боюсь, я не могу вынести ни единого слова, не потеряв истинного значения вопроса. Сначала я кратко опишу, чего я пытаюсь достичь, а затем расскажу, почему я хочу, чтобы это было сделано именно так. Пропустите бессвязную, если вы намерены прямо ответить на вопрос о названии вопроса: -)

Краткое описание

Предполагая существование встроенной lexicalize, которая делает именно то, что я хочу, вот так:

package Whatever;
{
  lexicalize $Other::Package::var;
  local $var = 42;
  Other::Package->do_stuff; # depends on that variable internally
}

Whys и whynots

Для некоторого контекста я тестирую whitebox стороннего модуля.

Мне нужна переменная localized , потому что я хочу, чтобы она изменялась только в течение ограниченного времени, прежде чем тесты перейдут к чему-то другому. local - лучший способ, который я нашел, чтобы сделать это, не завися от знания выбора модулем начального значения.

Я хочу лексическое связывание по двум основным причинам:

  1. Я не хочу загрязнять пространство имен верхнего уровня тестового файла.
  2. Я хочу что-то короче, чем полное имя. Для краткости его нет в примере кода, но я использую идентификатор намного больше, чем показано, с вычислениями и обновлениями относительно его предыдущего значения.

Я не смог прилично использовать our, потому что он не будет захватывать переменную из другого пакета: «Имя переменной не разрешено для переменной $ Other :: Package :: var в« our »». Обман для временного ввода Other :: Область действия пакета не обрезает его: если я использую блок ({ package Other::Package; our $var }), то привязка не длится достаточно долго, чтобы быть полезной; и если я этого не сделаю (package Other::Package; our @var; package main), то мне нужно знать и копировать имя предыдущего пакета, что предотвращает слишком частое перемещение этого фрагмента кода.

Выполняя домашнее задание, прежде чем задавать предыдущую форму этого вопроса, я обнаружил Lexical::Var, который, казалось, будет точно , что мне нужно. Увы: «Не удается локализовать с помощью ссылки».

Я также старался изо всех сил на интуиции форм на основе my *var, но продолжал сталкиваться с синтаксическими ошибками. Сегодня я узнал больше, чем хотел о том, чтобы определить объем и привязку: -)

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

Ответы [ 4 ]

3 голосов
/ 18 октября 2011

Есть несколько способов сделать это:

  • Дано:

    {package Test::Pkg;
        our $var = 'init';
        sub method {print "Test::Pkg::var = $var\n"}
    }
    
  • псевдоним переменной пакета из текущего пакета в целевой пакет.

    {
        package main;
        our $var;
    
        Test::Pkg->method; #     Test::Pkg::var = init
    
        local *var = \local $Test::Pkg::var;  
            # alias $var to a localized $Test::Pkg::var
    
        $var = 'new value';
    
        Test::Pkg->method; #    Test::Pkg::var = new value
    }
    
    Test::Pkg->method;     #    Test::Pkg::var = init
    
  • локализовать через глобальную ссылку:

    {
        package main;
    
        my $var = \*Test::Pkg::var;  # globref to target the local
    
        Test::Pkg->method; #     Test::Pkg::var = init
    
        local *$var = \'new value';  # fills the scalar slot of the glob
    
        Test::Pkg->method; #    Test::Pkg::var = new value
    }
    
    Test::Pkg->method;     #    Test::Pkg::var = init
    
  • связать лексическое в целевом пакете и позволить ему перетекать в текущий пакет:

    {
        package Test::Pkg;  # in the target package
        our $var;           # create the lexical name $var for $Test::Pkg::var
    
        package main;       # enter main package, lexical still in scope
    
        Test::Pkg->method; #     Test::Pkg::var = init
    
        local $var = 'new value';
    
        Test::Pkg->method; #    Test::Pkg::var = new value
    }
    
    Test::Pkg->method;     #    Test::Pkg::var = init
    

В последнем примере используется слегка неуклюжее объявление двойного пакета, чтобы создать лексический элемент в одном пакете и получить его в другом. Этот трюк полезен с local в других сценариях:

 no strict 'refs';
 local ${'Some::Pkg::'.$name} = 'something';
 use strict 'refs';
2 голосов
/ 18 октября 2011
package Other::Package;

$var = 23;

sub say_var {
  my $label = shift;
  print "$label: $Other::Package::var\n";
}

package Whatever;
Other::Package::say_var("before");
{
  local $Other::Package::var = 42;
  local *var = \$Other::Package::var;
  Other::Package::say_var("during");
  print "\$Whatever::var is $var inside the block\n";
}
Other::Package::say_var("after");
print "\$Whatever::var is ",
    defined($var) ? "" : "un", "defined outside the block\n";

Вывод:

before: 23
during: 42
$Whatever::var is 42 inside the block
after: 23
$Whatever::var is undefined outside the block

local $Other::Package::var временно делает исходное значение 42 на время блока, а local *var = \$Other::Package::var делает $var в текущем пакете псевдонимом для $Other::Package::var временно на время блока.

Естественно, некоторые из них не strict -совместимы, но я избегал использовать our, потому что our скрывает проблему, если оба пакета находятся в одном файле(как это может быть, если вы скопируете этот образец) - объявление our может просочиться из пакета, в котором оно используется, в следующий пакет в том же файле:)

2 голосов
/ 18 октября 2011

Я не совсем уверен, правильно ли я вас понимаю. Это помогает?

#!/usr/bin/env perl

{
    package This;
    use warnings; use strict;
    our $var = 42;

    sub do_stuff {
        print "$var\n";
    }
}

{
    package That;
    use warnings; use strict;

    use Lexical::Alias;
    local $This::var;

    alias $This::var, my $var;

    $var = 24;
    print "$var\n";

    This->do_stuff;
}

package main;

use warnings; use strict;

This->do_stuff;
2 голосов
/ 18 октября 2011

Если я вас правильно понял, все, что вам нужно, это наше.Извините, если я этого не сделаю.

Вот рабочий пример:

#!/usr/bin/env perl
use strict;
use warnings;

package Some;

our $var = 42;
sub do_stuff { return $var }

package main;

{
    local $Some::var = 43;
    print "Localized: " . Some->do_stuff() . "\n";
};

print "Normal: " . Some->do_stuff() . "\n";

Но если вы попытаетесь локализовать приватную (my) переменную какого-либо пакета, вы, вероятно, делаете что-то не так.

...