第16章 DBMファイルとDBMハッシュ 練習問題1,2,3

ファイルデータベースのDBMをPerl上でDBMハッシュとして使う.

1.perlfunc.podの中の=itemで始まる行の次に現れる識別子に対して,その識別子が初めて現れた行数を記録する

問題なが.perlfunc.podのフルパスは,

$ perlfunc -l perlfunc

で取得できる.中を見ると,

=item print FILEHANDLE LIST

などという行がいくつかあるのが見える.この,=itemで始まる行の=itemの次に現れる識別子がここで問題にされてるとこ.この例だとprintをキーに,その識別子が初めてあわわれた行数をDBMデータベースに保存することになる.

以下のようなコードを書いた.

#!/usr/bin/env perl -w
use strict;

# perlfunc.podのパスを得る
chomp(my $perlfunc_name = `perldoc -l perlfunc`);
# DBMファイルの名前を指定
my $dbname = 'words';

# perlfuc.podとDBMファイルをオープン
open PF, $perlfunc_name or die "cannot open $perlfunc_name: $!";
dbmopen (my %WORDS, $dbname, 0644) or die "cannot open dbm file: $!";

# DBMファイル内にもともとあったデータを消去
%WORDS = ();


while (<PF>) {
    # 正規表現で該当箇所をマッチ
    if (/^=item\s*([a-zA-Z0-9_]\w*)/) {
        my $name = $1;
        # $.は読み込んでいる行の番号
        $WORDS{$name} = $. if !defined($WORDS{$name});
    }
}


print "data stored.\n";

#クローズ
close PF;
dbmclose (%WORDS);

dbmopenでDBMデータベースを%WORDSというDBMハッシュに関連づける.このハッシュは記録されるデータを,DBMデータベースとしてファイルに保存することになる.で,実行すると,words.dbというファイルができた.これに,今処理した内容が保存されたわけだな.

この問題では,データベース作っただけで,使っていない.次の問題では実際に出来たデータベースを使用するプログラムを書く.

2. コマンドライン引数からしていされた関数がperlfunc.podの何行めに現れるかを表示

ついでに,つぎの問題もやろう.

3. 2の結果をもとにその行の位置をlessなどのpagerで表示する

1で作ったDBMファイルを読み込んで参照するだけ.以下のようなコードになった.

#!/usr/bin/env perl -w
use strict;

my $funcname = shift @ARGV;
die "Usage: $0 funcname\n" if !defined($funcname);

# perlfunc.podのパスとDBMファイルの名前を指定
chomp(my $perlfunc = `perldoc -l perlfunc`);
my $dbname = 'words';


# DBMファイルオープン
dbmopen (my %WORDS, $dbname, 0644) or die "cannnot open dbm file: $!";

# ここでDBMファイルから情報を取り出す
if (my $nline = $WORDS{$funcname}) {
    #結果を表示(練習問題 2に相当)
    print "$funcname is found at line $nline\n";

    # lessで表示することもできる (練習問題 3に相当)
    print "Print this by less?(y/n): ";
    chomp(my $ans = <STDIN>);
    if ($ans =~ /^\s*y/i) {
        !system "less +$nline $perlfunc" or die "error: $!";
    }
} else { # 見つからなかったら
    print "$funcname is not found\n";
}

# クローズ
dbmclose(%WORDS);

コマンドライン引数から指定された文字列をキーにDBMハッシュを探す.あれば,その値 = その識別子がperlfunc.pod内で何行目に初めて現れるか,を表示する.で,つぎにプロンプトが表示され,yesを答えると,perlfunc.podの見つけた行からをlessで表示する.なかなか便利に仕える感じ.perldoc -fとかも同じような実装なのか知らん?

ともあれ,DBMなどといってもopen,close以外は普通のハッシュと一緒なので簡単に使えそう.

おまけ

今回解答で使われてて,おっ,と思ったのは以下のようなコードだ.

$WORDS{$name} = $WORDS{$name} || $.;

# $WORDS{$name} = $. if !defined($WORDS{$name});これと同じ
の使い方が,かっこいい.$WORDS{$name}がundefの時は$.が$WORDS{$name}に記録されるが,undefじゃないと$WORDS{$name}の値はかわらない.デフォルト値の指定という奴だな.Rubyなどでも, =演算子でデフォルトの指定ができてこれはかっこいいと思った記憶がある.
$ irb
irb(main):001:0> words = {}
=> {}
irb(main):002:0> words['hakobe'] ||= 21
=> 21
irb(main):003:0> words
=> {"hakobe"=>21}
irb(main):004:0> words['hakobe'] ||= 100
=> 21
irb(main):005:0> words
=> {"hakobe"=>21}

しかし,こういうコードがぱっと思いつかないんだよね.精進精進.