2006 年 4 月 19 日 22 時 13 分

シンボリックリファレンス


このアーカイブは同期化されません。 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();

大きな壁はクリアした。



Copyright (c) 1994-2007 Project Loafer. All rights reserved.