「初めてのPerl」 16章 (単純なデータベース)

2007年の手帳はほぼ日手帳2007で良いかなぁ、と思っているbonlifeです。流行に踊らされたいです。「1日教養、1日休養」ということでお勉強、お勉強です。「初めてのPerl」の16章は「単純なデータベース」。RDBMSとかじゃなく、DBMですね。16章の内容はだいたい以下のような感じです。

  • DBMハッシュのオープン、クローズ (dbmopen, dbmclose)
  • DBMハッシュを使う (スキャンする時はeach関数使った方が良い)
  • packとunpackを使ってデータを加工
  • 固定長ランダムアクセスデータベース (seek, read)
  • 可変長(テキスト)データベース
  • コマンドラインから書き戻し編集 (-e, -p)

要するにハッシュをファイルに保存するってことですかね。なんか違うのかな。操作自体は特に難しくなさそうです。ということで早速練習問題のbonlife的(not 模範)解答例です。
ex16-1.pl

  • perlfunc.podファイルを読んで、その中から=item行に置かれている識別子名を探し出すプログラムを書く
  • 各識別子が登場する最初の行番号を記録したデータベースを作成する
  • 特殊変数$.には最後に入力した行の行番号が入っている
#! perl
use strict;
use warnings;

@ARGV = `perldoc -l perlfunc`;
dbmopen(my %DATA, "perlfunc_database", 0644)
  or die "Cannot create perlfunc_database : $!";
while (<>) {
    if ( /=item (.*?)\s/ ) {
        if (! defined $DATA{"$1"}) {
            $DATA{"$1"} = $. ;
        }
    }
}
#foreach my $key (keys %DATA) {
#    print "$key appears in the $DATA{$key} line.\n";
#}

dbmclose(%DATA);

出力結果はなし。解答例を見て気づいたことは以下の通り。

  • ファイルは@ARGV使って無理やりダイヤモンド演算子で読み込むよりも、ちゃんとファイルハンドラを使った方が良いような
  • 正規表現が雑過ぎる (^は必要)
  • %DATAの初期化(空にすること)を行っていない
  • definedを使う判定ではなく、||を使って前から順に評価することで「最初の行番号」の取得が可能
  • 処理完了後のメッセージがあった方が良い

簡単そう!と思って適当に書いた感丸出しです(恥)。4点目の || を使って前の処理が false の時のみ後ろの処理を実行する、みたいなのはかなりPerlっぽいですよね。このあたりを使いこなせるようになりたいです。そんなこんなで、2問目。
ex16-2.pl

  • コマンドラインからPerl関数の名前を1個受け取って、perlfunc.podファイルの中でその関数が最初に現れる=item行の行番号を表示するプログラムを書く
#! perl
use strict;
use warnings;

if ($#ARGV +1 != 1 ){
    print "Please set one function name to the argument.\n";
    exit 1;
}

dbmopen(my %DATA, "perlfunc_database", 0644)
  or die "Cannot open perlfunc_database : $!";

if ( defined $DATA{$ARGV[0]} ) {
    print "'$ARGV[0]' appears $DATA{$ARGV[0]} line firstly.\n";
} else {
    print "Function '$ARGV[0]' doesn't exist.\n";
}

dbmclose(%DATA);

出力結果は以下の通り。

ex16-2.pl shift
'shift' appears 4812 line firstly.
ex16-2.pl shifto
Function 'shifto' doesn't exist.

解答例を見て気づいたことは以下の通り。

  • このプログラムはデータベースが存在しなければ動作しないので、dbmopenの第3パラメータにはundefを指定した方が良い
  • || を使ってハッシュに値がある場合とない場合のメッセージを分けると格好良い

なるほど、といったところですね。(やっぱり || は使いこなしたい!)続いて、3問目。
ex16-3.pl

  • 問題2のプログラムを改造して、データベースから関数を見つけたら、ページャプログラムを起動して、perlfunc.podファイルのその行を見られるようにする
  • less +1234 filename のように起動するとそのファイルの1234行目を見ることができる
#! perl
use strict;
use warnings;

if ($#ARGV +1 != 1 ){
    print "Please set one function name to the argument.\n";
    exit 1;
}

my $file = `perldoc -l perlfunc`;

dbmopen(my %DATA, "perlfunc_database", 0644)
  or die "Cannot open perlfunc_database : $!";

if ( defined $DATA{$ARGV[0]} ) {
    my $start_line = $DATA{$ARGV[0]} - 1 ;
#    exec 'more', "+${start_line}", $file;
    system "more +${start_line} $file";
} else {
    print "Function '$ARGV[0]' doesn't exist.\n";
}

dbmclose(%DATA);

出力結果は以下の通り。

ex16-3.pl shift
=item shift ARRAY
X<shift>

=item shift
(中略)
^CTerminating on signal SIGINT(2)

なぜかexecを使うとmoreの動きがおかしかったので、systemを使いました。その結果、Ctrl+Cで終了したので、SIGINT(2)で終わってます。解答例を見て気づいたことは以下の通り。

  • ページャを起動する部分にも or die を書いた方が良い
  • execを使っている
  • ページャに渡す行番号はデータベースで見つかった行番号 (DOSコマンドのmoreの場合、データベースに格納した行番号をそのまま渡すと =item の次の行から表示されてしまったので、-1 してしまいました)

素直にUNIX(というよりLinux)環境でやった方が良い気がずーっとしてますが、まぁ、そこは気にしない方向で。時間はかかりましたが、なんとかここまでやってきました。残すは17章のみ。一気に片付けます!