この記事は、Perl Advent Calendar 2015の22日目の記事です。
なお、21日目はakihiro_0228によるプロジェクトのユニットテスト用雛形をサクッと作るでした。
最近、use MyModule
の後ろに関数名じゃないものを指定できるモジュールをいくつか使ったり、自分でも作ったりしたので、その時に調べたことをまとめました。
ただし、あんまり使いすぎると黒魔術になっちゃうので、ほどほどが良いようです。
モジュールについて
Perlではモジュールという仕組みが用意されていて、他人が作った機能を自由に自分のプログラムに取り込むことができるようになっています。例えば、Encode
モジュールが提供するencode
関数を使いたいときは以下のようにします。
use utf8;
require Encode;
my $text = 'ぱーるあどべんとかれんだー';
print Encode::encode('UTF-8', $text);
いちいちモジュール名を指定するのは面倒ですね。require
ではなく、use
を使うと関数名だけで実行できるようになります。
use utf8;
use Encode;
my $text = 'ぱーるあどべんとかれんだー';
print encode('UTF-8', $text);
ちょっと短くなりました。
あたかも自分で定義した関数と同じように、Encode
というパッケージ名を指定しなくても別のモジュールの関数が使えるようになりました。これはExporter
というモジュールで実現されています。
Exporterモジュール
上記のuse
すると関数名だけで呼び出せる仕組みは、Encode
モジュールでは、以下のようにExporter
モジュールを使って実現されています。
package Encode;
...
use Exporter 5.57 'import';
...
our @EXPORT = qw(
decode decode_utf8 encode encode_utf8 str2bytes bytes2str
encodings find_encoding clone_encoding
);
Exporterモジュールを使うと、@EXPORT
というパッケージ変数に関数名を与えておくことで、その関数名がuse
されるタイミングで利用者側のパッケージ名に取り込まれ、関数名だけで呼び出せるようにしてくれます。
いくつか使い方が有りますが、通常は以下のように継承して使うことが多いようです。
package MyModule;
use parent qw/Exporter/;
our @EXPORT = qw/func1 func2/;
sub func1 {
}
sub func2 {
}
1;
Exporterモジュールの実装
Exporter
モジュールのコードは以下のようになっています。
sub import {
my $pkg = shift;
my $callpkg = caller($ExportLevel);
...
# shortcut for the common case of no type character
*{"$callpkg\::$_"} = \&{"$pkg\::$_"} foreach @_;
}
パッケージの解説をゼロから始めると長くなるので割愛しますが、perlはuse MyModule qw/func1/;
を実行するタイミングで暗黙的にimport
という名前の関数を、qw/.../
で指定された内容をパラメータにセットして呼び出すようになっています。
Exporter
モジュールを継承しているとimport
メソッドが定義されているので、このimport
メソッドが呼び出され、関数がエクスポートされるわけです。
独自のimport関数
このようにExporter
モジュールを使っても良いですが、自前でimport
関数を定義する方法も有ります。
use
のタイミングでモジュールの挙動をパラメータによって変えたい時などに使います。
例えばTest::Requires
では、以下のようにuse
で指定するのはエクスポートしたい関数ではなく、テストに必要なモジュール名を指定するようになっています。
use Test::Requires qw/Test::Module/;
これはTest::Requires
のimport
関数を以下のように定義することで実現されています。
sub import {
my $class = shift;
my $caller = caller(0);
# export methods
{
no strict 'refs';
*{"$caller\::test_requires"} = \&test_requires;
}
# test arguments
...
for my $mod (@_) {
test_requires($mod, undef, $caller);
}
...
}
最初にtest_requires
という関数をエクスポートして、その後にパラメータで与えられているモジュールを引数にtest_reuqires
関数を呼び出しているのが分かるかと思います。
おわりに
通常はuse MyModule
の後ろはエクスポートしたい関数名を指定しますが、上記のように独自のimport
関数を定義してしまえば、何でもできてしまいます。本当にPerlは油断ならない言語ですね!
なお、次はpawaさんによる「青空文庫を支えるPerl言語」です!!