コード参照を取り込んでそのコードを一部のデータに適用する高階ユーティリティ関数がいくつかあります。これらの関数の一部は、サブルーチンの実行中に変数をローカライズする必要があります。最初は、この関数caller
の例に示されているのと同様の方法で、ローカライズするパッケージを決定するために使用していました。reduce
sub reduce (&@) {
my $code = shift;
my $caller = caller;
my ($ca, $cb) = do {
no strict 'refs';
map \*{$caller.'::'.$_} => qw(a b)
};
local (*a, *b) = local (*$ca, *$cb);
$a = shift;
while (@_) {
$b = shift;
$a = $code->()
}
$a
}
最初はこの手法は問題なく機能しましたが、高階関数の周りにラッパー関数を記述しようとすると、正しい呼び出し元を見つけるのが複雑になります。
sub reduce_ref (&$) {&reduce($_[0], @{$_[1]})}
今、動作するためreduce
に、私は次のようなものが必要になります:
my ($ca, $cb) = do {
my $caller = 0;
$caller++ while caller($caller) =~ /^This::Package/;
no strict 'refs';
map \*{caller($caller).'::'.$_} => qw(a b)
};
この時点で、どのパッケージをスキップするかが問題になり、それらのパッケージ内から関数を使用しないという規律が組み合わされました。より良い方法がなければなりませんでした。
高階関数が引数として取るサブルーチンには、問題を解決するのに十分なメタデータが含まれていることがわかります。私の現在の解決策は、B
イントロスペクションモジュールを使用して、渡されたサブルーチンのコンパイルスタッシュを決定することです。そうすれば、コードのコンパイルとその実行の間に何が起こっても、高階関数は常にローカライズする正しいパッケージを知っています。
my ($ca, $cb) = do {
require B;
my $caller = B::svref_2object($code)->STASH->NAME;
no strict 'refs';
map \*{$caller.'::'.$_} => qw(a b)
};
だから私の究極の質問は、これがこの状況で発信者のパッケージを決定する最良の方法であるかどうかです。私が考えていなかった他の方法はありますか?現在のソリューションで発生するのを待っているバグはありますか?