Perlで関数のリストを取得したい
とある事情からPerlで関数のリストを取得したくなりました.Module::Functions
というモジュールの存在は知っていたものの実装を知らなかったので眺めてみることにしてみました.
metacpan.org
コードはめちゃくちゃシンプルだけど読め...ない... まだまだ表層しか理解できていないという気持ちになりました😢
sub get_public_functions { my $klass = shift || caller(0); my @functions; no strict 'refs'; my %class = %{"${klass}::"}; while (my ($k, $v) = each %class) { next if $k =~ /^(?:BEGIN|UNITCHECK|INIT|CHECK|END|import)$/; next if $k =~ /^_/; next unless *{"${klass}::${k}"}{CODE}; next if $klass ne Sub::Identify::stash_name( $klass->can($k) ); push @functions, $k; } return @functions; }
%{"${klass}::"}
は一体なんだと思って調べてみたところ,文字(もしくはアンダースコア)で始まる識別子,つまり変数名や関数名が格納されたシンボルテーブルだとわかりました.
しっかりとperldocに書いてありました.perldoc.jp
パッケージのシンボルテーブルは、パッケージ名に二つのコロンを付けた名前の ハッシュに蓄えられます。 つまり、main のシンボルテーブルは %main::、または短く %:: となります。 同様に、先に述べたネストしたパッケージは %OUTER::INNER:: となります。
つまり,パッケージに属するシンボルテーブルを取得して,特殊なコードブロックや,_
で始まるプライベート関数を除いているんですね〜
とわかってきたところで次のわからないやつ *{"${klass}::${k}"}{CODE};
これは何だ...?
これの正体は型グロブというやつで,このサイトがめちゃくちゃわかりやすかったです.
要は,パッケージに属するシンボルテーブルから,型全てを表す*
をつけた*{"${klass}::${k}"
で取得して,型(SCALAR
,ARRAY
,CODE
など)をキーとしたハッシュが得られるわけですね.
そして,*{"${klass}::${k}"}{CODE};
は関数だけほしいので,キーにCODE
を指定していたわけです.
型グロブの経緯は
Perl は 型グロブ と呼ばれる内部型を、シンボルテーブルエントリ全体を 保持するために使っています。 この型グロブの型接頭辞は * です。 なぜなら、それが型全てを表すからです。 これは関数に対してリファレンスを使って配列やハッシュを渡すために 好んで使われていた方法でした。 しかし、私たちは今では本当のリファレンスを持っていますから、 型グロブを使う必要はほとんどありません。 perldata - Perl のデータ型 - perldoc.jp
ということで,リファレンスを実現するためのシステムだったのかと妙に納得しました.
以上から,シンボルテーブルからユーザー定義の関数を取ってくる
がこのモジュールのやってることだとわかりました.シンボルテーブルと型グロブという概念の勉強になったので良かったです.