モジュールの基本
1度書いたコードは再利用しなきゃもったいないですよね。同じ処理を何度も書いていたり、コピーアンドペーストしていたら、手間がかかりすぎます。
そういった再利用しそうなコードを書く時、Perlでは「モジュール」と呼ばれるファイルを作るそうです。コードをモジュールにする事で再利用しやすくなります。
ファイルの先頭で「package」を使って名前空間を定義して、ファイル名をそのパッケージ名と同じにします。また、普通のPerlスクリプトと区別するために拡張子には「.pm」を使います。
package Palmo;
というパッケージを宣言したなら、「Palmo.pm」として保存するわけですね。
また、パッケージ名に階層表現「::」が含まれている場合は、フォルダで階層を再現します。
package Palmo::Study::Perl;
と書いた場合、ファイルは「Palmo\Study\Perl.pm」(Windowsの場合)のように「Palmo\Study\」フォルダの中に「Perl.pm」として保存する事になります。
モジュールを使いたい場合、「use」を使って以下のように書きます。
use Palmo;
use Palmo::Study::Perl;
この場合、「Palmoモジュール」と「Palmo::Study::Perlモジュール」が利用できるようになります。利用者はモジュールの中身については何も知らなくてよく、「use すればどのような機能が使えるようになるか」だけを知ってれば良いわけですね。
use のかわりに require を使う事もできます。use と require の違いは、use がスクリプトのコンパイル時にモジュールが読み込まれるのに対し、require は実行時に読み込まれる、でしょうか。ラクダ本 第三版「プログラミングPerl VOL.1」 p351 には
一般に、requireよりも、useを使うほうが良い。なぜなら、useはコンパイル時にモジュールを読みに行くので、より早く間違いに気付くからだ。
と書かれていました。なるほど。
また、「use Palmo;」と書かれた時にPerlはPalmo.pmを探すわけですが、モジュールファイルを探すフォルダ(インクルードパス)は @INC に入っているとの事。
perl コマンドに「-e」オプションを使えば、1行スクリプトを実行できるので、@INC を表示してみました。
D:\dev\perl>perl -e "print join \"\n\", @INC;" C:/perl/lib C:/perl/site/lib .
「C:\perl\」に Perl をインストールしたので、「C:\perl\lib」と「C:\perl\site\lib」、それにカレントフォルダを表す「.」がインクルードパスになっています。
モジュールを作った時はこの中のどれかに置けばいいわけですね。
名前空間(2) package
大規模なプログラムを作る時に絶対に必要となる名前空間というものがあります。
プログラムを多人数で作ったり、モジュールに分割したりする時、皆がみんな好き勝手に変数を使って関数を定義していると、どうしても名前が衝突してしまいます。
「名前空間」という概念を導入すると、ある名前空間の中で宣言した変数や定義した関数の名前が、別の名前空間の中の変数や関数の名前と衝突しても、ちゃんと別の物として扱ってくれるようになります。
同じ「鈴木さん」でも「1年2組の鈴木さん」と「3年1組の鈴木さん」が区別できるように、頭に「?の」と付けるのと同じことですね。
Perlでは「package」という宣言をすることで、名前空間(パッケージ)を定義する事ができます。
package Palmo;
と書けば、以降のコードは Palmo 名前空間で実行される事になります。package 宣言で開始した名前空間は「レキシカルスコープ」(package宣言を囲むスコープ)の終わりまで、もしくは他のpackage宣言がされるまで有効です。
宣言された変数名や関数名などの識別子は、現在の名前空間に属します。ただし、「my」付きで宣言された変数は名前空間とは関係なく、そのスコープ内でのみ有効です。
今まで our で宣言した変数の事を「グローバル変数」と呼んでいましたが、実際には Perl にはグローバル変数は存在せず、どんな変数でも必ずいずれかの名前空間に所属しています(パッケージ変数)。
package Human;
our $animal = 'human';
の $animal と
package Lion;
our $animal = 'lion';
の $animal は全くの別物になります。
プログラムの開始直後は「main」名前空間が用いられるので、今までに僕が書いてきたスクリプトは、どれも main 名前空間の中で動いていた事になります。ちなみに、$_ や $! などの特別な変数は main 空間に属します。
パッケージ名に「::」(コロン2つ)を使う事で階層を表現できます。例えば
package School::Grade1::Class2;
と書けば「学校の中の1年の中の2組」という意味のパッケージを定義できます。
ただし、階層表現は人間だけに向けたもので、Perlにとっては「School::Grade1」と「School::Grade1::Class2」は全く関係ないものになります。
特定のパッケージの識別子を表現するには、識別子の前に「パッケージ名::」と書きます。例えば「$School::Grade1::Class2::suzuki」と書けば「School::Grade1::Class2」パッケージに所属する「$suzuki」という変数を表します。また、パッケージ名を省略して「$::hoge」のように書くと「$main::hoge」と同じ意味になります。
このように識別子のパッケージを特定する事を「修飾する」(qualify)と言います。
階層表現はPCでのファイルシステムに似ていますね。フォルダがパッケージ、ファイルが識別子みたいです。(^_^)
パッケージの動作を確かめてみます。まず、ファイルを3つ作ります。
# human.pl
package Human;
our $animal = 'human';
# lion.pl
package Lion;
our $animal = 'lion';
# tiger.pl
package Tiger;
our $animal = 'tiger';
our でそれぞれ $animal というパッケージ変数を宣言しています。ただし、package で名前空間を変更しているので、これらの変数は別のものであるはずです。
# packtest.pl
require 'human.pl';
require 'lion.pl';
require 'tiger.pl';
package Human;
print "package Human : $animal\n";
package Lion;
print "package Lion : $animal\n";
package Tiger;
print "package Tiger : $animal\n";
がメインとなるファイルです。「require」を使うと他のPerlスクリプトファイルを読み込む事ができます。(requireについては後で詳しく調べます) C言語の #include のようなものでしょうか。
その後、それぞれの名前空間で $animal の中身を表示しています。
以下が実行結果です。
package Human : human package Lion : lion package Tiger : tiger
ちゃんと違う値が表示されました。(^_^)
最初は1つのファイルでやろうと思ったのですが、修飾せずに $animal とだけ書くと、パッケージ変数よりもより近くで宣言された同名の変数が優先されてしまうので、ファイルを分割しました。
上の例は以下とほぼ同じ意味ですね。
# packtest2.pl
package Human;
our $animal = 'human';
package Lion;
our $animal = 'lion';
package Tiger;
our $animal = 'tiger';
package main;
print "package Human : $Human::animal\n";
print "package Lion : $Lion::animal\n";
print "package Tiger : $Tiger::animal\n";
違いは print する際に「$human」とだけ書くか、パッケージ名を修飾しているかどうかです。