5

長いタイトルですが、質問の真の意味を失わずに一言も聞き出せないのではないかと心配しています。最初に何を達成しようとしているのかを簡単に説明し、次になぜこのようにしたいのかについて長々と説明します。質問のタイトルを順守して直接回答する場合は、とりとめのないものをスキップしてください:-)

簡単な説明

私が望むことだけを行うビルトインが存在すると仮定すると、次のlexicalizeようになります。

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

理由と理由

ちょっとした文脈のために、私はサードパーティのモジュールをホワイトボックステストしています。

テストが別のものに移る前に、限られた時間だけ変数を変更したいので 、変数をローカライズしたいと思います。local初期値に対するモジュールの選択を知ることに依存せずにこれを行うことがわかった最良の方法です。

字句バインディングが必要な主な理由は 2 つあります。

  1. テスト ファイルの上位の名前空間を汚染したくありません。
  2. 完全修飾名よりも短いものが必要です。簡潔にするためにサンプルコードには含まれていませんが、以前の値に関連する計算と更新を使用して、示されているものよりもはるかに多くの識別子を使用しています。

our別のパッケージから変数を取得しないため、適切に使用できませんでした。一時的に Other::Package のスコープに入るための不正行為はそれをカットしません: ブロック ( { package Other::Package; our $var }) を使用すると、バインディングは有用であるほど長く持続しません。そうでない場合はpackage Other::Package; our @var; package main、前のパッケージの名前を知ってコピーする必要があります。これにより、そのコードの一部を移動しすぎるのを防ぐことができます。

この質問の前の形式を尋ねる前に宿題をしているときに、まさに私が必要とLexical::Varしていたものだと思われるを発見しました。悲しいかな: 「参照を通じてローカライズできません。」</p>

また、- ベースのフォームの直感で最善を尽くしましたmy *varが、構文エラーにぶつかり続けました。今日はスコープとバインドについて、思ったよりも多くのことを学びました :-)

私が望んでいることが不可能な理由が思い浮かびませんが、適切な呪文が見つかりません。残念ながら実装されていないエッジケースを求めていますか?

4

4 に答える 4

3

これを行うにはいくつかの方法があります。

  • 与えられた:

    {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';
于 2011-10-17T21:22:09.550 に答える
2
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使用を避けました —宣言は使用されているパッケージから漏れる可能性がありますで、同じファイル内の次のパッケージに:)ourourour

于 2011-10-17T21:29:14.743 に答える
2

私があなたを正しければ、あなたが必要とするのは私たちだけです。私がしなければ許してください。

これが実際の例です:

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

しかし、あるパッケージの private ( my) 変数をローカライズしようとすると、おそらく何か問題が発生します。

于 2011-10-17T20:33:00.920 に答える
2

あなたのことを正しく理解しているかどうか、よくわかりません。これは役に立ちますか?

#!/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;
于 2011-10-17T21:14:27.347 に答える