私は、コード参照を取り込んでそのコードをいくつかのデータに適用するいくつかの高次ユーティリティ関数を持っています。これらの関数の中には、サブルーチンの実行中に変数をローカライズする必要があるものがあります。この例ではreduce
機能を示すように、初めに、私は同様の方法で、にローカライズするためにどのパッケージを決定するためにcaller
を使用していた。Perlでは、コードレットのパッケージを決定する最も信頼性の高い方法は何ですか?
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
}
は、最初は、この技術は、しかし、すぐに私はラッパーを書いてみましたように、うまく働きました高次関数の周りで機能し、正しい発信者が複雑になることを理解する。それは機能を使用したことがないの規律と組み合わせて、スキップするパッケージをの質問になりました。この時点で
my ($ca, $cb) = do {
my $caller = 0;
$caller++ while caller($caller) =~ /^This::Package/;
no strict 'refs';
map \*{caller($caller).'::'.$_} => qw(a b)
};
:
sub reduce_ref (&$) {&reduce($_[0], @{$_[1]})}
は今reduce
が動作するためには、私のようなものが必要になりますそれらのパッケージから。より良い方法がなければならなかった。
問題を解決するのに十分なメタデータが引数として取られていることが分かります。私の現在の解決策は、B
イントロスペクションモジュールを使用して、渡されたサブルーチンのコンパイルstashを判断することです。そのようにして、コードのコンパイルとその実行の間に何が起こっても、高次関数は常に正しいパッケージがローカライズされることを知っています。
my ($ca, $cb) = do {
require B;
my $caller = B::svref_2object($code)->STASH->NAME;
no strict 'refs';
map \*{$caller.'::'.$_} => qw(a b)
};
これは、このような状況では、呼び出し側のパッケージを決定するための最良の方法であるのであれば、私の究極の質問はありますか?私が考えていない他の方法がありますか?私の現在のソリューションで起こるのを待っているバグはありますか?
これは実装に依存しているようです...今後のバージョンのPerlではこれが変更されないと自信が持てますか?それぞれのオブジェクトに関数を格納させ、適切なパッケージを覚えておくことは、生の関数の代わりにオブジェクトを使用する方が簡単で堅牢ではないでしょうか? – Nemo