始めに
学生時代にPerlを始めて数年経った程度の人間が、Perlについて如何に無知であったか、戒めと反省の意を込めて書いておきます。
この記事は、次のような方向けです。
- Perlなんて楽勝
- Perlなんて見るのも嫌
また、この記事で言いたいことは、次の2つです。
- 思い上がりは良くない
- Perlが悪いわけではなく、Perlを書く人間が悪い
この文章を読むための前提知識
この反省文を読むために必要な知識です。
ですが、難しいことは何もありません。
「初めてのPerl」とは
初めてのPerl(以下リャマ本)は、オライリー・ジャパン社が発行した、Perl初心者のための入門書です。
原書は英語版であり、上記の本は日本語訳版です。
英語版は第7版が最新版として存在しますが、日本語訳版は第6版が最新版です。
「続・初めてのPerl」とは
続・初めてのPerl(以下アルパカ本)もまた、オライリー・ジャパン社が発行した本であり、リャマ本の続編にあたります。
こちらも原書は英語版であり、上記の本は日本語訳版です。
英語版、日本語訳版共に、第2版が最新版です。
そのほか
オブジェクト指向(OOP)やモジュールの作り方、テストコードやバイオインフォマティクスなどについては省略します。
アルパカ本を読む前には何をしていたのか
リャマ本の第3版、第5版、第6版を読んでいました(ちょくちょく内容が変わっていて面白いです)。
また、バイオインフォマティクスのためのPerl入門と実践 バイオインフォマティクスもざっと読んで、BioPerlを利用したバイオインフォマティクスなどもしていました(割愛しますが、遺伝子配列のBLASTやデータベース運用などです)。
オブジェクト指向のPerlモジュールも、簡単なものなら作っていました(この件については後述します)。
テストコードも作っていました(これは大したテストまでしてませんが、せいぜいモックを作ったOS依存テストだったり、例外テストとかです)。
リャマ本の内容は一通り頭に入れていましたし、ファイル処理や正規表現によるテキスト処理などもしていました。
わからない処理についてはその都度検索してきました。
Perlプログラマのレベル10で言えば、僕はレベル7に当たると考えていました(自惚れていました)。
アルパカ本を読んでから何が無知であるかを自覚したのか
見る世界が変わりました。
同時に、僕はPerlを何も知らないのだと思い知らされました。
Perlのオブジェクト指向を何も理解していなかった
実は、リャマ本の内容だけでもある程度のことは可能です。
これが非常に大きいのです。
リャマ本で基本的な機能は使えるようになります。
更に、手続き指向ライクな書き方でも1000行を超えるプログラムの作成は可能です(実際に僕は2000行弱のプログラムを、なんと1つのファイルで作りました)。
ですが、実際に1000行を手続き指向で作った場合、大抵はスパゲッティプログラムになるのが関の山です1。
また、「Perl オブジェクト指向」で検索して出てくるものが、次のようなコードばかりであることにも拍車をかけています。
皆さんも、オブジェクト指向ライクなPerlを書こうと思って、次のようなコードを見たことはありませんか?
package PerlOop;
use strict;
use warnings;
sub new {
my $class = shift;
my $self = {
value => 0,
};
return bless $self, $class;
}
sub value {
my $self = shift;
$self->{value} = shift;
$self->{value};
}
sub calculate_twice_value() {
my $self = shift;
$self->{value} * 2;
}
1;
もちろん、これはあくまで例です。
ここにメソッドを追加したり、継承クラスを追加したりするでしょう。
ですが、オブジェクト指向のPerlは、最低でもこれくらいは書けるようにならないといけないことになります。
面倒ではないですか?
これで「やっぱりPerlはオブジェクト指向に向いてない」だなんて考えて諦めたら、元も子もありません。
実は、Mooseというモジュールを使えばかなり簡単に書けるようになります23。
package MooseOop;
use Moose;
has 'value' => ( is => 'rw', default => 0 );
no Moose;
__PACKAGE__->meta->make_immutable;
sub calculate_twice_value {
my $self = shift;
$self->value * 2;
}
1;
さて、どう変わったでしょうか?
根本的な箇所は省略して、ここでは大きな箇所を見ていきます。
まず、new
メソッドを書く必要がなくなりました。
これでわざわざコンストラクタを明記する必要ないモジュールには new
を書くという面倒な作業を削減できます。
次に、has 'value' => ( is => 'rw', default => 0 );
という文章が出てきました。
これは、メンバ変数の定義文です。
is
パラメータでは、可視性の設定ができます。
'rw'
という文字通り、ここではvalue
が読み書き可能であることを設定しています。
これが'ro'
であれば、読み取り専用となり、直接書くことができません。
また、default
パラメータでデフォルト値の設定が可能です。
そして、セッターとゲッターを書く必要がなくなりました。
なぜなら、簡易的なセッター、ゲッターはMooseが自動で作ってくれるからです。
その使い方は、calculate_twice_value
メソッドを見ればわかると思います。
もともと$self->{value}
で直接値を使っていたのが、メソッド経由で使えるようになったわけです。
勿論、$self->{value}
が使えなくなったわけではありませんが、is
パラメータが'ro'
の場合、$self->{value} = 1
のようなコードはエラーが発生します。
最後にわかりずらい(しかし重要なこと)ですが、use strict; use warnings;
が消えました。
なんと、Mooseを使う場合は自動で有効にしてくれます。
これだけでは正直、大したことがないかと思うかもしれません。
しかし、Mooseを使うとかなり様々なことが可能です。
例えば、次のようなモジュールを見てください。
package MooseOopManager;
use Moose;
extends 'MooseOop';
has 'name' => ( isa => 'Str', required => 1 );
no Moose;
__PACKAGE__->meta->make_immutable;
1;
MooseOopモジュールを継承したMooseOopManagerモジュール(extends 'MooseOop'
と書いている文が継承を意味しています)に登場してもらいました。
このモジュールのhas 'name' => ( isa => 'Str', required => 1 );
の2つのパラメータを見てください。
1つ目のisa
は、型制約です。
つまり、メンバ変数name
は、文字列でなければいけません。
2つ目のrequired
は、オブジェクトを生成する際に必ずメンバ変数を用意しなければならない、という意味です。
次のようなコードではエラーが発生します。
use MooseOopManager;
my $obj1 = MooseOopManager->new; # nameの初期値を設定していない
my $obj2 = MooseOopManager->new( name = 1 ); # nameの初期値が文字列でない
これをMoose無しでどのように実装するのでしょうか?
ひな形生成ツールの存在を知らなかった
もしかして、モジュールを作るとき、あるいはテストコードを作るときに、0から.pmファイルや.tファイルを作っている人はいますか?
僕は今まで、既存のモジュールをコピペして1から作り直していました4。
しかし、コマンド1つで簡単なひな形を作ってくれるツールがあると便利だと思いませんか?
実はあるんです。
それが、Module::Starterと呼ばれるディストリビューションツール56です。
これはディストリビューションツールと言う名の通り、単なるひな形生成ツールではなく、Makefile.PLやBuild.PLファイル、.tファイル、更にREADMEまで作ってくれます(CPANモジュールと同じ形式です)。
使い方はいたってシンプルです。
$ module-starter --mb --module=Hoge --author="Morichan" \
--email=mori@earth.cs.miyazaki-u.ac.jp --verbose
このコマンドを打つだけで、上の例なら、カレントディレクトリ直下にHogeディレクトリを作り、その中に Hoge.pm
というPerlモジュールとかテストコードとか Build.PL
とかREADMEとかを階層構造で作ってくれます。
しかも、module-starter設定ファイルを作れば、更にコマンドは短くなります。
author: Morichan
email: mori@earth.cs.miyazaki-u.ac.jp
builder: Module::Build
verbose: 1
.module-starter
ファイルのおかげで、次のコマンドで同じ内容を生成します。
$ module-starter --module=Hoge
詳しい使い方は調べてみてください。
ちなみに、Module::Starterは標準モジュールではないので、手動で導入する必要があります78。
CPANやPerlmonksを無視していた
今までCPANは「よくわからない雲の上の存在」と認識していました。
Perlmonksについては、その存在も知りませんでした。
実は、リャマ本とアルパカ本は、著者Randal Schwartzが生徒への教科書として使っているようです。
そして、アルパカ本の最終章では、CPANに投稿してみようという題目があります。
この章があるということ自体に僕は驚きました。
同時に、英語圏の人々にとってCPANは、単なる1サイトでしかないのだなと感じました。
僕が今までPerlを勉強したのは、あくまで日本のサイトです。
ですが、英語が読める人間は英語のサイトを読むのです。
そこには、何十億人という分母の人間のプログラマの知恵が詰まっています。
一方、日本語で調べても、分母は1億人です。
例えば、BioPerlを日本語で解説しているサイトはほとんどありませんが、英語であれば、かなり多くのページがヒットします。
試しにStack Overflowの日本語版サイトと英語版サイトで「bioperl」を検索した結果を比較すると、日本語版では0、英語版では379と出ました。
もちろん、母数が増えたからといって欲しい情報が手に入るとは限りませんが、不利ではあるでしょう。
結論
知りたいことがあれば、その単語を検索窓に入れればわかります。
しかし、知りたいことが何かわからない場合、そして存在を知らない場合、その答えを知ることは容易ではありません。
僕は、アルパカ本でMooseとModule::StarterとCPANとPerlmonksの存在を知ることができました。
アルパカ本には、リファレンスやテストについても載っていますから、これらを知らない人にとっても有益な本だと考えられます。
まだまだ世の中に知らないことは沢山あるんだと気づかされました。
ここまで読んでくださって、ありがとうございます。
もしおかしい点、気づいた点がありましたら教えてください。
特に、コードは適当に書いたので、誤字脱字あると思います。
-
僕がスパゲッティプログラムを作ってしまったのは、リファレンスをまだ理解していなかった時に、グローバル変数として配列やハッシュを読むような構造にしたせいなのですが... ↩
-
ちなみに、Moose自体はかなり大きなモジュールなので、簡易的なプログラムを作るときはMooやMouseを使うべき、とのことです(アルパカ本より)。↩ -
(mackee_w さんより)Mooseは依存が多く、メンテナンス頻度がMooやMouseと比べて低いため、むしろMooseよりはMooやMouseを使うほうがいいようです。また、型システムが必要なく、プロパティアクセスのメソッドが欲しいだけなら、Class::Accessor::Liteも考えたほうがいいらしいです。 ↩
-
「0から~」と「1から~」の文章は、Perlの真偽値の話をしているわけではありません。 ↩
-
h2xsやModule::Buildなどもありますが、機能が弱いらしいので省略します。 ↩
-
(mackee_w さんより)Minillaというものもあるようです。 ↩
-
次のコマンドで導入してください。
$ cpan Module::Starter
↩ -
(mackee_w さんより)最近はcpanよりcpanmを使うことのほうが多いそうです。cpanmを使い場合も同様に
$ cpanm Module::Starter
で導入できます。しかし、標準モジュールではないので、コマンドがないと言われた場合は次のコマンドでcpanmを導入してみてください。$ cpan App::cpanminus
↩