プリプロセッサを利用したプログレスインジケータの試み
ユーザを待たせる長い処理を行う場合には、進行状況をユーザに知らせるプログレスインジケータを出すのが作法である。Perlベストプラクティス では、ユーティリティサブルーチンを使う方法 (10.16 節) と Smart::Comments を用いる方法 (10.17 節) を紹介している。以前関わったプロジェクトでは、次のような条件があり、どちらも使えなかった
なので、次のような解決策を用いた。
- コード中に特殊なコメントの形でプログレスインジケータを挿入する場所を示す
- コードを本番用に変換するフィルタを作り、それを通してから実行する
このほど、似たような状況になったのだが、ふと、Perl には C 言語のプリプロセッサを通す -P コマンドがあったことを思い出した。ので、プリプロセッサを利用したプログレスインジケータを作ってみた。
処理の始まりのところに配置する _BEGIN_PHASE には、標準エラー出力に表示する処理の名前 name と、後に示す _CONTINUE_PHASE を何回実行したら '.' を表示するかを表す interval を指定する。処理を繰り返すところには _CONTINUE_PHASE を置く。処理終了の場所には _END_PHASE を置き、処理終了を表示させるようにする。
use strict; use warnings; #define _BEGIN_PHASE(name,interval) my ($_c1,$_c2,$_int)=(0,0,interval); print {*STDERR} name #define _CONTINUE_PHASE $_c1++;if($_c1>=$_int){$_c2++;if($_c2>=10){print {*STDERR} '*';$_c2=0;}else{print {*STDERR} '.'}$_c1 = 0;} #define _END_PHASE print {*STDERR} "done\n" sub heavy_process { use Time::HiRes qw/usleep/; usleep(1 + rand(10)); } _BEGIN_PHASE('testing',100); for my $i (0..100_000) { _CONTINUE_PHASE; heavy_process; } _END_PHASE;
実行するときには -P オプションをつける。
[takeyuki@sunya ~]$ perl -P a.pl test.........*.........*.........*..(略)...*done
複数回使うためには、別のブロックに入れなければならない、などセンシティブな面も多く、実行時のコストもかかるが、ちょっと凝ったプログレスインジケータが必要な場合などに応用が効くのではないだろうか。