概要: Perl でレキシカル変数を外部のスコープから変更する Tips
外部スコープからレキシカル変数の値を変更する、 という普通んなことしねーよな変態なことをやりたかったので、 やる方法をだいぶ前に調べたことをまとめてみる。
結論から言えば PadWalker を使えばできる。
使い方はまあこんな感じ。細かいことは POD 見れば分かるので説明は省略。
use strict;
use warnings;
use PadWalker qw( peek_my );
use Perl6::Say;
{
my $x = 'foo';
say $x; # 'foo'
&magic();
say $x; # 'bar'
}
sub magic {
my $vars = peek_my(1);
${ $vars->{'$x'} } = 'bar';
}
で、まあ何でレキシカル変数を外部から書き換えるとか変態的なことがしたかったかというと、 blosxom のプラグインの設定を プラグインファイル自体を書き換えずに 変更したかったから。 要するにプラグインの設定を外部ファイルに一まとめにしたかったって言う。
blosxom のプラグインは設定がpackage
の直下my
で定義されてるので普通は外から変更できない。
で、最初peek_my
でできるかと思ってたんだけど、色々やってみても結局できなかった。
んで、どうしたもんかとやっていたうちに、 peek_sub
使えばできるんじゃね? というのを思いついて、
実際に試してみたらできちゃったという。大体こういう感じでできた。
package foo;
my $confA = 'AAA';
my $confB = 'BBB';
sub start {
return 1;
}
package bar;
use PadWalker qw( peek_sub );
my $start_sub = foo->can('start');
my $vars = peek_sub( $start_sub );
${ $vars->{'$confA'} } = 'BBB';
${ $vars->{'$confB'} } = 'BBB';
どういうことかと言うと、$confA
,$confB
はpackage
直下で定義されてるので、 関数start
内のスコープからでも参照できる 。
んで、peek_sub
は 関数のスコープ内のレキシカル変数を取得する ことができるので、
レキシカル変数を参照できるスコープを持つ CODE reference を取得し、その CODE reference からpeek_sub
を使ってレキシカル変数を取得できる というわけ。
言葉にするとややこしい気がしないでもないけど、コード見れば大体分かると思う。
まあそういうことが分かったのでまあプラグインの設定を外部ファイルに切り出すプラグインはできたものの blosxom いじりが途中で止まってるので 公開してなかったりする。もうちょっといじって CodeRepos に上げる予定はしてるけど。
なんか久しぶりに空繰再繰更新した。もうちょっと更新頻度上げたい。
追記
どうも嘘書いてたっぽい 。
今日気付いたんだけど、上記の方法では サブルーチンで参照していない変数 は参照できないもよう。
どうしたもんかな、これ。
#FIXME