このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。
昨日の考察により、通常の手段によって、
文字列で受け取ったパッケージのシンボルテーブルに
新しくシンボルを追加することは難しいことが判明した。
しかし方法がないわけではない。
Perl には、特別なモジュールの作成を助けてくれる、
シンボリックリファレンスというものがあるのだ。
シンボリックリファレンスを使えば、
シンボルテーブルに存在するあらゆる値を、
「文字列」によって示す名前を使って参照することができる。
百聞は一見にしかず。例を挙げて試してみよう。
まず、パッケージ変数 value を定義してみる。
これは、$main::value であり、
シンボルテーブルでは、$$::main{'value'} となる。
our $value = 150;
そして、通常の文字列 $ref を定義する。
my $ref = 'value';
ではこの時点で以下のようにするとどうなるだろうか。
print $$ref;
実は、以下のように表示される。
150
これが不思議でないと思う人は頭が柔らかい。
$$ref は、$ref というリファレンスに対して、
スカラ変数としてデリファレンスを行なう書き方だ。
この場合 $ref はリファレンスではなく、文字列である。
ここで、$ref がシンボリックリファレンスとして機能する。
$ref は 'value' であるため、文字列を展開し、
$$ref は、$value として解釈されるのだ。
シンボリックリファレンスと言う名前は、
シンボルテーブルを指すという意味と、
Unix におけるシンボリックリンクのように、
名前で他のものを指す役割があるからである。
複雑な例を出そう。
package Pack1;
our $value = \150;
package Pack2;
our $extern = \'Pack1::value';
package main;
my $ref = \'Pack2::extern';
print $$$$$$ref;
$ref は文字列 'Pack2::extern' へのリファレンスだ。
$$ref は文字列 'Pack2::extern' である。
$$$ref は、$$ref がリファレンスでなく文字列なので、
シンボリックリファレンスとして機能し、
$$$ref は、$Pack2::extern として解釈される。
これは、Pack2 パッケージの extern スカラを指し、
値は、文字列 'Pack1::value' へのリファレンスだ。
$$$$ref は文字列 'Pack1::value' だ。
$$$$$ref は上記同様、$Pack1::value となり、
値 150 へのリファレンスとなる。
$$$$$$ref は、値 150 だ。
上は意地汚い例ではあるが、
シンボリックリファレンスを使えば、
その値を名前としてシンボルテーブルを検索し、
実際の値をデリファレンスして取り出すことができる。
シンボリックリファレンスは、スカラだけでなく、
配列や連想配列などにも利用できる。
use Data::Dumper;
my $main_symbol_table_name = 'main::';
print Dumper(\%$main_symbol_table_name);
どうだろうか、文字列さえあれば、
あらゆるパッケージにアクセスできることになるのだ。
これは、非常に危険な行為であり、
プログラムの見通しが悪くなるという問題もある。
そのため、通常のプログラミングにおいては、
シンボリックリファレンスは使用しないのが好ましい。
strict プラグマ的モジュールを use しておくと、
シンボリックリファレンスは禁止され、
シンボリックリファレンスが使われたことを検出すると、
エラーが発生するようになる。
たとえ、特殊なモジュールであっても、
シンボリックリファレンスの利用は必要最小限に留め、
一般的なリファレンス(ハードリファレンス)を使って
コードを書くべきである。
strict モジュールは、no に対応しているため、
コードの一部分で以下のように指定することで、
一時的にシンボリックリファレンスを使うことができる。
no strict 'refs';
さて、シンボリックリファレンスを使うと、
他のパッケージにメソッドを登録することが簡単にできる。
use strict;
no strict 'refs';
my $explicit_name = 'Pack3::method';
*$explicit_name = sub { print "method called!\n" };
Pack3::method();
大きな壁はクリアした。