17章(上級テクニック) 練習問題

次のようなプログラムを書いてください。まずファイルから文字列のリストを読み込みます(1行が1個の文字列になっています)。次に、ユーザがキーボードから対話的に入力したパターンを文字列にマッチさせる、という処理を繰り返します。入力されたパターンが、ファイルに入っていた文字列何個にマッチしたか、及び実際にマッチした文字列を表示するようにしてください。パターンを新た入力するたびに、ファイルを読み直すというやり方はしないでください。−全ての文字列をメモリ上に(つまり変数に)保存しておいてください。ファイル名はプログラム中に直接書いてしまって構いません。もしパターンが正しくなければ(例えば、括弧の対応が取れていない、など)、そのエラーを報告してから、ユーザーに再びパターンを入力してもらうようにします。ユーザがパターンの変わりに空行を入力したら、プログラムを終了するようにしてください。
初めてのPerl p299-300

初めてのPerl、最後の練習問題です。
気を引き締めてやってみました。

#!/usr/bin/perl
use strict;
use warnings;

#$input_fileの全内容を配列へ読み込む
my $input_file = 'sample_text';
open(INPUT, "<$input_file") or die "$input_fileをオープンできませんでした: $!";
my @lines = <INPUT>;
close INPUT;

#メインルーチン
print "文字パターンを入力してください。\nプログラムを終了したい場合は、何も入力しないでください。\n\n";
while (chomp(my $word = <STDIN>)) {
    die "プログラムの実行を終了します。お疲れ様でした。\n" if ($word eq '');

    eval { 

	#マッチ処理の開始
	my $count = 0;
	for my $line (@lines) {
	    if ($line =~ /$word/) {
		$count++;
		print "$line";
	    }
	}
	print "$wordは全文字列中の$count個にマッチしました。\n\n";
    };

    if ($@) {
	print "不正な文字パターンです。\n[$@]\n再度文字パターンを入力してください。\n\n";
    }

    print "文字パターンを入力してください。\nプログラムを終了したい場合は、何も入力しないでください。\n\n";
}

print "プログラムの実行を終了します。お疲れ様でした。\n"

実行結果はこちらです。

文字パターンを入力してください。
プログラムを終了したい場合は、何も入力しないでください。

max
maxは全文字列中の0個にマッチしました。

文字パターンを入力してください。
プログラムを終了したい場合は、何も入力しないでください。

min
coming. get cleaned up, because we're going."
the bathroom to see if wilma was coming. whoops! he tripped over
"fred flintstone, you let me see that mouse this minute!" cried
department of, uhm, mineral, uhm, transportation. i'm calling for
"mister bubble from the department of mineral transportation."
wilma was getting more curious. fred said, "well, that mineral, i
"but fred," said barney, "it's a bowling ball, not a mineral."
minは全文字列中の7個にマッチしました。

文字パターンを入力してください。
プログラムを終了したい場合は、何も入力しないでください。

barney
for fred and barney in lowercase, as we usually do. some lines
one evening, fred and barney were getting ready to go bowling.
wilma saw this and said to betty, "you should ask barney to take
betty agreed. "barney and fred never think about us. we should
"but wilma," said fred, "barney and i need to go tonight. this is
meanwhile, at the rubbles' house, barney was having the same
"oh, barney! you are so sweet. of course you can go bowling. do
barney checked the time. "we aren't scheduled to start until half
"uhm, yes." said barney on the other end of the line, disguising
"heya, fred," said barney. "betty and i have figured out what to
barney continued. "you and wilma go ahead to the restaurant.
"but fred," said barney, "it's a bowling ball, not a mineral."
she pulled the phone away from fred in time to hear barney say,
"barney, is that you?" she asked the phone.
"yep. i mean," said barney, hastily disguising his voice again,
heard someone named barney on the line. but there is no one named
barney here. no barney at all. put that bowling ball down,
barneyは全文字列中の17個にマッチしました。

文字パターンを入力してください。
プログラムを終了したい場合は、何も入力しないでください。

///,,[[[
不正な文字パターンです。
[Unmatched [ in regex; marked by <-- HERE in m////,,[ <-- HERE [[/ at C:\Perl\hajip\ex17-1.pl line 24, <STDIN> line 4.
]
再度文字パターンを入力してください。

文字パターンを入力してください。
プログラムを終了したい場合は、何も入力しないでください。

(())))))))))
不正な文字パターンです。
[Unmatched ) in regex; marked by <-- HERE in m/(())) <-- HERE )))))))/ at C:\Perl\hajip\ex17-1.pl line 24, <STDIN> line 5.
]
再度文字パターンを入力してください。

文字パターンを入力してください。
プログラムを終了したい場合は、何も入力しないでください。


プログラムの実行を終了します。お疲れ様でした。

C:\Perl\hajip>

問題の要求通りに実装できているし、大丈夫だな。
さてさて、解答はどうなっていることやら。
解答チェック。


うむ。やっていることはほぼ同じ。
けれどもエレガントさじゃあ到底敵わねえ。
なんといってもいけてないのはこの箇所。

    eval { 

	#マッチ処理の開始
	my $count = 0;
	for my $line (@lines) {
	    if ($line =~ /$word/) {
		$count++;
		print "$line\n";
	    }
	}
	print "$wordは全文字列中の$count個にマッチしました。\n\n";
    };

この箇所を解答ではgrepとmapを用いて、スマートに記述してます。
自作コードが単一行フォーカスで問題解決しているのに対し、解答コードは全行フォーカスで問題解決している、といった感じでしょうか。


それでは書き直しいってみます。

#!/usr/bin/perl
use strict;
use warnings;

#$input_fileの全内容を配列へ読み込む
my $input_file = 'sample_text';
open(INPUT, "<$input_file") or die "$input_fileをオープンできませんでした: $!";
my @lines = <INPUT>;
close INPUT;

#メインルーチン
print "文字パターンを入力してください。\nプログラムを終了したい場合は、何も入力しないでください。\n\n";
while (chomp(my $word = <STDIN>)) {
    die "プログラムの実行を終了します。お疲れ様でした。\n" if ($word eq '');


    my @matched_lines = eval {
	grep /$word/, @lines;
    };

    if ($@) {
	print "不正な文字パターンです。\n[$@]\n再度文字パターンを入力してください。\n\n";
    }
    else {
	my $counts = @matched_lines;
	print "$wordは全文字列中の$counts個にマッチしました。\n\n";
	print "マッチした文字列を表\示します:\n",
	    map "\t$_", @matched_lines;
    }

    print "文字パターンを入力してください。\nプログラムを終了したい場合は、何も入力しないでください。\n\n";
}

print "プログラムの実行を終了します。お疲れ様でした。\n"

改良部分は解答コードほぼそのまんまwですが断然ブラッシュアップされましたね。
それでは実行結果をば。

文字パターンを入力してください。
プログラムを終了したい場合は、何も入力しないでください。

max
maxは全文字列中の0個にマッチしました。

マッチした文字列を表示します:
文字パターンを入力してください。
プログラムを終了したい場合は、何も入力しないでください。

min
minは全文字列中の7個にマッチしました。

マッチした文字列を表示します:
        coming. get cleaned up, because we're going."
        the bathroom to see if wilma was coming. whoops! he tripped over
        "fred flintstone, you let me see that mouse this minute!" cried
        department of, uhm, mineral, uhm, transportation. i'm calling for
        "mister bubble from the department of mineral transportation."
        wilma was getting more curious. fred said, "well, that mineral, i
        "but fred," said barney, "it's a bowling ball, not a mineral."
文字パターンを入力してください。
プログラムを終了したい場合は、何も入力しないでください。

barney
barneyは全文字列中の17個にマッチしました。

マッチした文字列を表示します:
        for fred and barney in lowercase, as we usually do. some lines
        one evening, fred and barney were getting ready to go bowling.
        wilma saw this and said to betty, "you should ask barney to take
        betty agreed. "barney and fred never think about us. we should
        "but wilma," said fred, "barney and i need to go tonight. this is
        meanwhile, at the rubbles' house, barney was having the same
        "oh, barney! you are so sweet. of course you can go bowling. do
        barney checked the time. "we aren't scheduled to start until half
        "uhm, yes." said barney on the other end of the line, disguising
        "heya, fred," said barney. "betty and i have figured out what to
        barney continued. "you and wilma go ahead to the restaurant.
        "but fred," said barney, "it's a bowling ball, not a mineral."
        she pulled the phone away from fred in time to hear barney say,
        "barney, is that you?" she asked the phone.
        "yep. i mean," said barney, hastily disguising his voice again,
        heard someone named barney on the line. but there is no one named
        barney here. no barney at all. put that bowling ball down,
文字パターンを入力してください。
プログラムを終了したい場合は、何も入力しないでください。

///,,[[[
不正な文字パターンです。
[Unmatched [ in regex; marked by <-- HERE in m////,,[ <-- HERE [[/ at C:\Perl\hajip\ex17-1.pl line 18, <STDIN> line 5.
]
再度文字パターンを入力してください。

文字パターンを入力してください。
プログラムを終了したい場合は、何も入力しないでください。

(())))))))))
不正な文字パターンです。
[Unmatched ) in regex; marked by <-- HERE in m/(())) <-- HERE )))))))/ at C:\Perl\hajip\ex17-1.pl line 18, <STDIN> line 6
]
再度文字パターンを入力してください。

文字パターンを入力してください。
プログラムを終了したい場合は、何も入力しないでください。


プログラムの実行を終了します。お疲れ様でした。

C:\Perl\hajip>

何も変わらず。
大丈夫ですね。


それでは最後の練習問題終わりー!
お疲れ様でした。


さてと、最後の締めとして総括でも書くかな。