今、話題の人工知能(AI)などで人気のPython。初心者に優しいとか言われていますが、全然優しくない! という事を、つらつら、愚痴っていきます

048.プログラミングで大切な事

»

初回:2019/09/18

1.最近、エンジニアの話が少なくなってます?

P子「疑問じゃなくって、事実でしょ」(※1

 と言う事で、久しぶりにプログラミングの話題です。

P子「エンジニアの話じゃなくって、プログラミングなの?」

 皆さん、それぞれにプログラミングするにあたり、重要視していることがあると思います。

 納期、工数とかも大切ですが、ここで言いたいのは、汎用性、処理速度、メモリ使用量、行数、堅牢性、バグの数(テスト工数)、開発手法、保守性...、などの項目の優先付けをどうしているのかと言うのが、今回のテーマです。

P子「バグの数って、0でしょ」

 理想的にはそうですけど、残るものは残るんです!

P子「あ、開き直った」

 課題:1から10までの整数を加算した合計を表示する、最速のプログラムを作成しなさい。

 皆さんは、どのようなコーディングを考えたでしょうか?

 まずはメソッド(関数)を作成しますか? 引数を1つ与えて1からnまでのループを回して足し算しますか? 普通なら、n*(n+1)/2 の公式を使用するでしょうね。

 汎用性を考慮するなら、引数を3つ与えて等差数列の和の公式を使用しますか?
 初項 a、末項 b、項数 n の等差数列の和は、n*(a+b)/2 です。

 項数の代わりに項差 dを使用するなら、項数 n=(b-a)/d+1 なので、((b-a)/d+1)*(a+b)/2 という風に式を変形して、初項 a、末項 b、項差 d で計算する方が判りやすいかもしれません。

 関数を作る場合、各項目が正の整数であることのチェックと、初項と末項の大きさチェックが必要です。項差 d=0 なら、forループなら無限ループですし、公式なら、0での割り算になってしまいます。

 不正な入力が入った場合、どうするのが良いでしょうか?

 例えば、初項 a と 末項 b が逆転している場合に、エラーにして中止すべきか、入れ替えて計算すべきか、どちらが良いでしょうか?
 また、3.0 という整数ではない入力値の場合、処理を続けるべきか、警告を出して中止すべきか...

 また、int型で定義する場合は、答えが int の範囲内かどうかも検討が必要です。long型なら大丈夫という物でもありません。javaの場合は、明確に整数値の最大値が決められています。python2も、int型は制限がありますが、python3からは、無制限になりました。(long型と同じになった)

 また、バッチ取り込みのような入力データが、数万行もある場合、1行づつ、先の計算をしている場合に、途中で不正なデータが見つかった場合、そこで止めるべきか、それまでの計算全てを破棄すべきか、エラーデータだけ除外ファイルに書き出しておいて、続きの処理を実行すべきか...
 これは、データベースにおけるトランザクションの考え方に基づきます。

 そろそろ、この『課題』に対する『正解』をお見せしましょう。

 回答:print(55)

P子「なんじゃそりゃ?」

『合計を表示する、最速のプログラム』なんですから、これ以上の『正解』があれば、コメントください。

P子「じゃ、私はアセンブラで書こうかな」

 ちゃちゃを入れないでください。

2.プログラミングで大切な事

 先の『課題』に対する『正解』をコーディングするプログラマはまずいないでしょう。つまり、課題に対しての『正解』は『最適解』ではない(可能性)があるということです。

 先ほどのプログラムは、非常に簡単な課題だったにもかかわらず、きちんとしたプログラムを作成しようとすると、結構難しいことをご理解いただけたかと思います。

 つまり、プログラミングにおける『最適解』とは、何を重要視してコーディングするかと言う事だと思います。

・汎用性
 1から10までの整数の和を求める関数に汎用性を持たせる場合、初項 a=1、末項 b、項差 d=1 を固定にして、引数として末項 b だけ与える関数にすべきか、項差 d=1 だけ固定にすべきか、引数を3つ持つ関数にすべきか...。これは、過去の使用実績やシステムの種類にも依存します。

・処理速度
 ある程度は考慮すべきですが、最速にチューニングするのは逆に悪い事だと思います。何より、コンピュータの処理速度は何もしなくても早くなりますし、お金でも買えます。

・メモリ使用量
 処理速度と同様に、ある程度の考慮で十分でしょう。もちろん、メモリが少ない組込系では優先順位が上がるでしょうけど、サーバー上で動作する基幹系システムでは、優先順位は下がります。

・行数
 メソッド内の行数を見える範囲に限定するとか、そういう意味では重要です。しかし、Code Golf の様に短ければよいという物ではないでしょう。

 課題:最短のFizzBuzzコードを作成しなさい。
 回答:for i in range(100):print(i%3//2*"Fizz"+i%5//4*"Buzz"or-~i)

 Python3 で(59 bytes)だそうです。

・堅牢性
 2つの意味があって、ややこしいですが、1つはインジェクション攻撃などの脆弱性に対する対策の事です。もう一つは、ユーザーの誤った入力や不正な文字に対して、プログラムがおかしな反応をしてしまわないという耐性のことです。これの重要性は結構高いと考えています。

・バグの数(テスト工数)
 バグと知っていて残す人は居ないでしょうけど、潜在バグが残っている可能性を少しでも潰すためには、テスト工数を確保する必要があります。これをどれくらい行うか、コーディング時間よりもかかる可能性があります。これも重要性は高いです。

・開発手法
 ウォーターフォール、アジャイル、プロトタイピング、データ駆動開発、エクストリーム・プログラミング...などなど。どれが良いかは、組織の考え方や対象となるシステム、言語など色々な要件によって決まります。重要性は高いと思いますが、組織的な動きが必要なので、変えるのは大変かもしれません。

・保守性
 これも色々と解釈可能です。ソースコードが読みやすい、コメントがきっちり書かれているという意味と、改造が容易で、顧客の業務変更に対応しやすいなども、保守性が高いプログラムの優位点と言えます。

 さて、保守性を一番最後に持ってきたという事は、私が一番重要だと思っているのが、保守性だという事です。

P子「なんじゃそりゃ?」

 私には、好きなおかずを最後に食べる習性があります。

P子「昔、おばあちゃんに『好きなものから食べないと、地震が来たら食べられなくなるわよ』って言われたわよ」

 2位は堅牢性で、3位はバグの数(テスト工数)です。(※あくまで個人の感想です)

 2位の堅牢性と3位のバグの数が逆じゃないの?という意見もあるかと思いますが、堅牢性...ユーザーの不正な操作や異常な入力値に対して、きちんと動作する作りと言うのは、バグに対しての耐性も上がると思っています。良くある、仕様とバグのハザマ問題に対しても、堅牢性の対応で仕様漏れが明確になるかもしれません。
 何より、作った後でテストでバグを潰すより、作る前にバグとなりそうな箇所を潰す方が前向きだと思うので、堅牢性を2位にしました。

 もちろん、バッチによる一括処理のような場合、処理速度やメモリ使用量と言うのは重要なファクターになってきます。大量バッチ処理で「ORA-01555 スナップショットが古すぎます」エラーに泣かされた人も少なくないと思います。

P子「自動UNDO運用にしても、ディスクのパンクを恐れて自動拡張をOFFにすると、ORA-30036: UNDO表領域内でセグメントを拡張できませんが出ちゃったりして...」

 それでも、昔に比べれば、だいぶましです。

 これらのエラーへの対応は、堅牢性の範疇です。

3.重要度の判定基準

 1位:保守性
 2位:堅牢性
 3位:バグの数

 という順位ですが、要するにプログラムを開発する時間というのは、予め決められておりある程度は自分のペースで行えます。疲れたら、翌日に作業を行えばよいのです。

 ところが、実運用が始まって、トラブルが発生すると、即対応が求められます。

 出荷伝票が出なくて、工場の外の道路にまでトラックが並んで大渋滞を引き起こした経験が無いでしょうか?

 異常が出ない堅牢性も重要ですが、何か異常が発生したとき、すぐに直せる(自分以外の開発者がコーディングしていたとしても)保守性は、時間との勝負の時に一番効果が発揮されます。これは、精神衛生上とても貴重です。異常時の即対応は、ユーザーにとっても結果的に良いプログラムと言う事になります。

ほな、さいなら

======= <<注釈>>=======

※1 P子「疑問じゃなくって、事実でしょ」
 P子とは、私があこがれているツンデレPythonの仮想女性の心の声です。

Comment(1)

コメント

白栁隆司

僕もその3つが重要だと考えています。

コメントを投稿する