>   >  痴山紘史の日本CG見聞録:第35回:より良いプログラムを書くために[使いやすいプログラムのつくり方]
第35回:より良いプログラムを書くために[使いやすいプログラムのつくり方]

第35回:より良いプログラムを書くために[使いやすいプログラムのつくり方]

みなさんこんにちは。先月購入した3Dプリンタを使って新しく3Dプリンタをつくり始めました。何を(ry という感じですが、ソフトウェアとちがって現物がガシャコンガシャコンと動く機械をつくるのは楽しいです。まずは既存のものをほぼそのままつくってみて、そこから自分なりに改良していけたらいいなと目論んでいます。皆さんも一丁、マィ・3Dプリンタ製作に挑戦してみませんか!?

TEXT_痴山紘史 / Hiroshi Chiyama(日本CGサービス
EDIT_尾形美幸 / Miyuki Ogata(CGWORLD)



使いやすいプログラムのつくり方

前回は、良いプログラムを書くってどういうこと? という具体例を、既存コードのリファクタリングを通してご紹介しました。今回は、使ったときに不安にならない、ストレスの元にならないプログラムのつくり方をまとめていきます。

ユーザーへのフィードバック

長い時間がかかる処理を行う際、ユーザーに対して定期的に何らかのフィードバックを返してプログラムが生きていることを伝えるのはとても大事なことです。アプリケーションの操作中にGUIが固まってしまったものの、CPUの使用率は高いままなので、不安は棚上げしてとりあえず放置するという経験を誰もがしたことがあるでしょう。せめて、このときに何かログがながれて処理が進んでいることが確認できれば、不安感を大幅に軽減することができます。可能であれば、全体に対し、どの程度まで処理が進んでいるのかを確認できるようになっていれば、ユーザーは安心して待つことができます。

ただし、伝える情報は必要十分なものにして、伝える順番も気を付けます。

例として、ディレクトリにあるファイルの名前がルールに沿っているかチェックするプログラムを作成するとします。

for f in os.listdir(path):
    if check_filename(f) == False:
        print('invalid filename : %s' % f)
    else:
        print('valid filename : %s' % f)

これはユーザーにプログラムが死んでいないことを知らせるという目的は果たしていますが、あまり良くない例です。最終的にユーザーが必要としているのは不正なファイル名はどれかという情報なのに、正常なものも同列に表示してしまっているため、エラーがあるのかどうか、ログを見ただけで把握するのは難しくなっています。もしディレクトリに十万個のファイルがあって、その中にひとつだけ問題のあるファイルが含まれていた場合、ユーザーは高い確率でエラーを見逃してしまうでしょう。これでは本末転倒です。

そこで、ユーザーに伝える情報を絞ってエラーだけ出力するようにしてみます。

for f in os.listdir(path):
    if check_filename(f) == False:
        print('invalid filename : %s' % f)

もし、check_filename が非常に重たい処理で、かつファイルが大量にある場合はどうでしょうか?

不正なファイルがあった場合は、ファイルが不正だという表示が出るのでプログラムが実行されていることはわかりますが、それ以外のときには何のフィードバックもありません。1分くらいで処理が終わるのならいいですが、数十分もかかるようなものの場合、とてもではないですが待っていられません。

このような場合、進捗状況がわかるようにした上で、最後に必要な情報を提示するようにします。

errors = []
files = os.listdir(path)
for i,f in enumerate(files):
    print('check file [%.2f%%]: %s' % (i*100.0 / len(files), f))
    if check_filename(f) == False:
        print('  invalid!!')
        errors.append(f)

print( 'invalid files:')
print( '\n'.join(errors))

そうすれば、全体に対し、どのくらい処理が進んでいるのか、どの程度エラーになっているのかがリアルタイムにわかり、かつログの最後を見るだけで不正なファイルを把握することができます。

ユーザーはリアルタイムに状況を把握できるため、場合によっては処理を途中で停止して問題を修正した上で再度実行することもできます。

レスポンスタイムの改善

長時間の事前処理を行なってから本番処理を行うようなケースでは、事前処理に時間をとられ、本番処理の確認が進まないことがあります。 このような状態は大きなストレスになるため、処理を組み替えて早い段階で結果を取得できるようにします。

図にすると以下のようになります。


事前処理を一度に行うようなプログラムのつくり方をしてしまうと、肝心の本番処理でエラーになったときに、事前処理の時間が丸々無駄になってしまいます。 更に、エラーを修正して二度目の実行を行うときに大きなちがいが出てきます。


長時間の事前処理を行うプログラムの場合、再度事前処理が実行されてしまいます。こうなってしまうと、問題修正→確認というサイクルを高速に回すことができません。

これに対して、内容を分割して順番に処理するようにしたプログラムでは、既に処理が終了している部分は全てスキップして、エラーになったところから実行できます。このような構成になっていれば、たとえ再度エラーになったとしても即座に結果がわかり、早いサイクルで問題対応ができます。事前処理に20分かかり、本番処理のチェックは1分で済むような場合には、分割して処理した場合との差は非常に大きなものとなります。

さらにプログラムを改良して高速化しようとした場合、処理内容が整理されて分割されていれば、個々の処理を複数同時に実行して全体の処理速度を稼ぐといった工夫も可能です。


処理内容が整理されていれば、処理の組み換えや整理をして、さらなる高速化を図ることも簡単に行えます。


次ページ:
不幸な事例