カラクリサイクル

『輝かしい青春』なんて失かった人の雑記

Perlでレキシカル変数を外部からいじる方法

概要: 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,$confBpackage直下で定義されてるので、関数start内のスコープからでも参照できる。 んで、peek_sub関数のスコープ内のレキシカル変数を取得することができるので、 レキシカル変数を参照できるスコープを持つCODE referenceを取得し、そのCODE referenceからpeek_subを使ってレキシカル変数を取得できるというわけ。 言葉にするとややこしい気がしないでもないけど、コード見れば大体分かると思う。

まあそういうことが分かったのでまあプラグインの設定を外部ファイルに切り出すプラグインはできたもののblosxomいじりが途中で止まってるので 公開してなかったりする。もうちょっといじってCodeReposに上げる予定はしてるけど。

なんか久しぶりに空繰再繰更新した。もうちょっと更新頻度上げたい。

追記

どうも嘘書いてたっぽい

今日気付いたんだけど、上記の方法ではサブルーチンで参照していない変数は参照できないもよう。

どうしたもんかな、これ。