Perlにも少し慣れてきたので、気に入っている点と不満な点を書いてみよう。まず、気に入っている点から。
何と言ってもこれ。単に正規表現が使えるというだけではなくて、正規表現が使いやすいように言語自体が作られているということ。デフォルト変数という変数の記述の省略法と合わせて使えば、短いコードでかなり複雑な処理も記述できる。正規表現はとくにテキスト処理で有用で、標準で正規表現が使えない言語と比べるとそのあたりのコードの量がかなり違ってくる。
UNIX系OSをはじめ、Windowsやその他のOSでもPerlの実行環境を用意すれば、同じコードがそのまま使えることが多い(機種に依存したことをしていない場合)。PerlはもともとUNIX系OSとともに発展してきたという経緯があるので、UNIX系OS(ウェブサーバーでよく使われる)でほぼ確実に使え、CGIの記述用にもサーバーにインストールされている言語を気にしないで気軽に使用することができる。PHPやRubyなどの新しい言語も最近では多くのサーバーでインストールされていることが多いけれど、まだそういう点ではPerlに少し長があるでしょう。
また、Windows版のPerl(ActivePerl)の移植度が高いのもよい。ファイルロック関数などOSレベルでサポートされていない機能もあったりするけれど、だいたいはUNIX系OSと同じコードがそのまま使える。
Javaのような割と厳格な記述しか許されていない言語に比べて、Perlでは同一の処理をするのにかなり多様な記述法ができる。デフォルト変数という、変数の記述を省略したときに暗黙で採用される変数の機能を使えばさらに短く記述でき、冗長性を省いた記述もできる。この点は、厳格な記述法にも多くの利点があるのでどちらがよいとは言えないけれど、ちょっとしたプログラムを書く場合には短く書けるのは便利だ。それなりにまとまったプログラムの場合でも、(好みの問題だけど)適切に短いコードを書くことによってコードの可読性が上がる場合があるように思う。たとえばJavaの場合はif文の条件式ではbool値しか使えない(たしか)ので、(my_valueがBoolean型以外の場合)
if (my_value)
と書くことはできず、
if (my_value != 0)
などと明示的に書いてやる必要がある。Perlの場合は式の値を暗黙のうちに真・偽にして判断してくれ、さらに配列などの場合は要素数が評価の対象になるので、
if (@items)
のような記述もできる。また、
open(IN, 'myfile.dat') or die;
などのような記述は簡潔で便利。
上にも書いたけれど、これは好みの問題で、しかも厳格な型チェックを行ってくれると発見しにくいバグを予防できるので、大規模なプログラム、機械制御などの厳密な動作(記述)が求められるプログラムなどでは厳格な型チェックの方が適しているように思う。
複雑なデータ構造を変数で扱うとき、C/C++やJavaなどでは最初に適切なクラスを用意し、メモリを確保してやるなどの準備が必要だけど、Perlの場合には自動的に判断され、配列への要素の追加などが簡単に記述できる。たとえば、
my %record;
push(@{$record{'男'}{'長水路'}{'自由形'}{'100m'}}, ['山田 太郎', '59.23']);
というコードでは、最初にハッシュ型変数recordを宣言し、次の行でいきなり複雑なデータ構造が用意されているような感じで代入(配列への追加)がされている。かなり読みにくいコードだけど、次のようなデータ構造ができている。()内は取りうる値。
record
sex (男, 女)
course (短水路, 長水路)
style (自由形, 背泳ぎ, 平泳ぎ, ...)
distance (50m, 100m, ...)
[name, time],
[name, time],
...
これを参照する場合は、たとえば
print ${$record{'男'}{'長水路'}{'平泳ぎ'}{'200m'}}[0][0];
とすれば一人めのnameが表示される(あってるかな?)。{}の中には変数が使え、要素数も簡単に取得できるので、実際にはループで回して全体を処理できる。
複数行のリテラル文字列を記述するときに、ヒアドキュメントを使えば簡潔に記述でき、さらにダブルクォーテーションやクォーテーションの記号をエスケープしなくてもよい。
my $body = <<'END'; 1行目 2行目 ... END
実行時に文字列をPerlの文として評価(実行)するという関数 eval が用意されている。これを使えば、できそうでないと思ったことができたり、普通に書けば複雑になりそうなことが簡潔に書けたりすることもある。ある意味とても強力(強力すぎるほど)な機能。ただ、多くの場合、やっている処理はかなりトリッキーなものになる。
それほど重要ではないかもしれないけれど、文字列中に変数の値を展開できるので、コードが読みやすくなったり、記述が簡潔になったりすることがある。とくに、ヒアドキュメントと組み合わせると簡潔に記述できることがある。
my $html = <<END;
<title>$title</title>
<h1>$title</h1>
<div align=right>更新日:$date</div>
${\join("<br>\n", @body)}
END
最後の ${\join("<br>\n", @body)} というのは、配列変数を文字列中に展開するテクニックで、@bodyの各要素を <br>\n で繋げて代入している。
日本語などのマルチバイト文字をPerlで処理する場合、マルチバイト文字内のバイトにエスケープコードが含まれていたりすると正しく処理できない問題がある。Shift JISでもEUCでもこのような問題はある。マルチバイト文字を組み込みレベルで文字単位で扱えない、というくらいならまだよいのだけど、プログラムが正しく解釈されなくて、コンパイルエラーや実行時エラーになるのはかなり不便。
これはPerlの問題というよりも、僕個人の問題という気もするけれど。
Perlは昔からある言語で、オブジェクト指向の機能も途中から追加されたものであるため、最初から設計されている言語よりも記述法の点で幾分スマートでない点があると思う。Perlの本を何冊か読んでみたけれど、オブジェクト指向の使い方についてはいまいち分からなかった。理解力が足りないのか、読んだ本がまずかったのか…。Javaなどのときにはクラスの作り方・使い方などは直感的に理解できるものだったのだけど…。
Perlでのオブジェクト指向、要勉強。
Javaのクラス内のクラスのような感じでサブルーチン内のサブルーチンを記述することができない。テクニックを使えば、それらしいことは一応できるのだけど、そういう機能をもった仕様の言語に比べてはきれいじゃない記述になってしまう。サブルーチン内のサブルーチンは、たとえば次のように記述する:
sub main {
my $val = 2;
my $innersub = sub {
return $val * $val;
};
print "$val の2乗は", $innersub->(), "です。\n";
}
トップダウン式の記述法で記述する場合、内部のサブルーチンはできれば後ろのほうに書きたいけれど、呼び出すときに $innersub が未定義だとエラーになってしまう。
CGIとしてPerlを使った場合、アクセスがあるごとにPerlを起動するのでそのオーバーヘッドがある(普通の用途なら気になるほどではないけれど)。PerlをApacheの組み込みモジュールとして、常時起動しておくという方法もあるようだけど、その場合には、変数の初期化がされていない?などの(小さな)問題もあるという記述を見かけたりするので、今のところ使う気にはならないなぁ。ただ、オーバーヘッドはPerlだけの問題ではないけれど。
PHPなどの場合はもっとスマートな処理がされているのかな。
しばらく使ってみて思ったのは、やっぱり大規模だったり、厳密な記述がしたい場合にはJavaなどの方がしっくりきそうだなということ。ただ、普段の事務処理的な用途やCGIなどなら、わりと高度なことが簡単に記述できるPerlは強力。とくに正規表現が手軽に使えるのは強力で、ちょっとしたテキスト用のフィルタならちょこちょこっと書いてすぐに使える。
PerlでGUIなプログラムを作るためのTkライブラリは使ったことがない。GUIというと、これもやっぱりJavaなどで書く方がしっくりくる気がするなぁ。
個人的には、純粋なオブジェクト指向言語(Javaなど)とこういう手軽なスクリプト言語の2種類は必要だなぁ。
あまり本格的に使っているわけではないけれど、現時点で思っていること。
Perlと同じような感じで、要素の追加などが行える。JavaScriptの場合はあまりよく試していないけど、Perlでは上記のように一気に深い階層への代入ができるので、Perlの方が強力かな。
Perlと同じで、JavaScriptのeval関数も強力そう。ただ、個人的にはほとんど使ったことがない。
JavaScriptでメンバ関数を書く場合、
function myClass() {
...
var myFunc = function() {
...
};
}
とすると、インスタンスごとの関数になってしまう。たとえば、
var a = new myClass();
var b = new myClass();
b.myFunc = function() { return false; };
とすると、a.myFuncとb.myFuncはそれぞれ別のものになる(正確には、最初から違う実体のものだけど、内容は同じだった)。これは、インスタンスごとに異なる関数を持つことができ、実行時にも変更できるので使い道はありそうだけど、インスタンスを大量に作る場合にはメモリを無駄に消費してしまう。
メンバ関数を書く場合には次のようにする:
function myClass() {
...
}
myClass.prototype.myFunc = function() {
...
};
これで、myClassのインスタンスが個別の関数myFuncを持たない場合にはprototypeで指定したmyFuncが使われることになる。
ただ、好みの問題かもしれないけれど、Javaのようにクラス内部にメンバ関数を書きたいな…。
処理に時間がかかるプログラムだったりすると、Internet Explorerの場合には「このまま処理を続けますか?」というようなプロンプトが表示される。処理速度もそれほど速くない(?)ような感じがするので、やはり大規模なプログラムには向いていないのかなぁ。
ブラウザでの実行を主な目的としている(?)ので、当たり前なのだけど、他のプログラムと連携させて使うというようなことができにくい。Javaアプレットとなら少しは連携させられるようだけど。
これは、個人的な使用の場合には自分が使うブラウザを限定してしまえばよい話。しかし、公開用のウェブページでJavaScriptを使う場合(この用途がほとんどだと思う)では、このあたりが大きな問題になる。最近のブラウザではかなり解消されているのかな?(最近はIEでしかテストしなくなった)。
ポップアップ広告などを除去する等の機能をもったProxyソフトをユーザーが使っていると、HTML中のJavaScriptのコードが書き換えられてブラウザに渡されることになり、結果的に思い通りの処理がされないことになってしまう。
ただ、このあたりまで気にしていては何もできないなぁ…。