[ソフト開発][ネタ]わかりやすいプログラムの書き方

※このエントリは、Arata Kojima/NPO法人しゃらく さんが公開しているわかりやすい技術文章の書き方の改変です。

このページは、プログラムやコードなどを書く方々のために、分かりやすいプログラムを書くためにはどうすればよいのかについて説明しています。
 1. 自分が伝えたいこと・訴えたいことを誤解しないように相手に読んでもらうにはどうするべきか。
 2. プログラムを書くにあたって知っておくべきルールは何か。
 3. プログラムを書く前にどのような手順を踏めば、分かりやすいプログラムを作れるか。
などについて参考にしていただければ幸いです。

プログラムを書く前に

プログラムを書く前に次のことをしっかりとイメージしておく。

  • 何を書くのか。
  • 書こうとしている物は正確に何であるのか。
  • 仮定して良い、必ず成り立つ前提(状況/状態)は何か。
  • 成り立つ事が単に多いだけ/今はたまたま成り立っている、という仮定してはならない前提(状況/状態)は何か。
  • 核となる概念・構成物は何か。

プログラムを知る

プログラムとは何か
  1. 現実世界の課題が与えられ、または自分が課題を提起し、
  2. その課題に対して明確な解を与え、
  3. その課題を取り巻く現実世界を正確に定義する語彙を作成して、それによって解を記述する。

cf. 「プログラム=動きを書く物」 は集団幻想

標準的な構成要素とは何か

プログラムの構成は、言語により様々であるが

  • モジュール・パッケージ・名前空間
  • クラス・インタフェース・モジュール
  • メソッド・関数
  • ブロック・コード片

という要素が標準的である。次にそれぞれの要素について簡単に見てみる。

モジュール・パッケージ・名前空間
互いに強く関連するクラス・インタフェース・モジュールをまとめる。
クラス・インタフェース・モジュール
 
  • 提供するメソッド・関数によって実現するただひとつの責務は何か。
  • 責務に対する考え方は明確で十分単純であり、かつ、一貫しているか。
  • その責務を果たすために本当に依存すべき前提は何か。
  • 状況依存や前提を切り離して、責務がより単純にならないか。
    • 必須ではない他のクラス・インタフェース・モジュールに依存していないか。依存先を上位の型に変更できないか。
    • 必須ではない値を保持・利用していないか。依存先の型に任せられないか。
メソッド・関数
 
  • 引数に対して返却すべき物は何か。それは明確であり、かつ、一貫しているか。
  • 切り離せる状況依存や前提を抱え込んでいないか。引数に追い出せないか。引数の型を上位の型に出来ないか。
  • 必須ではない非ローカルの変数(グローバルやクラスなど)を利用していないか。引数に追い出せないか。
  • 記述のレベルは詳細過ぎないか、具体型に依存し過ぎてないか。意味が明確な下位関数に切り出してそれを利用して書けないか。引数側に任せられないか。
  • 複数の事をしていないか。別関数に出来ないか。
  • 所属している上位要素(クラス・モジュール)は適切か
ブロック・コード片
変数の意味を明確かつ一貫させ、スコープを最小にする 

そのほかに、プログラムの利用のためにはプログラムのドキュメント・マニュアル、確認にはテストなどを書く。

プログラムを組み立てる

プログラミングにおける語彙とは何か

プログラミング=対象の分析、プログラム=対象の定義


プログラムとは、日本語や英語ではなくプログラミング言語を使って、「対象」=実現しようとしている事 の定義」を書いたもの。対象が大きければ、対象を分析して、定義に必要なプログラミング言語上の語彙を増やす。もちろん、その語彙とは名前。そして、名前だけじゃなくて、それが示すものの定義をプログラミング言語で書く。こうやって語彙を積み上げていって、実現しようとしている事の定義を書く事がプログラミングに他ならない。


プログラムを書き始めるときに使える語彙は、環境に用意されたライブラリしかない。その語彙を利用して組み合わせて、新しい語彙の定義を書く。そうやって新しく作った語彙、環境に用意されている語彙を組み合わせて、対象の定義を書くのに必要な新しい語彙を積み上げる。そうやって作った、対象にフィットした語彙を使って、実現しようとしていること全体の定義を書いた物がプログラム

そのソースコードが汚い理由:共通した根源的な間違い - よくわかりません
プログラムを組み立てる順番
課題を取り巻く現実世界を細分化する
 

プログラムを書くときにはまず実装を省いた概略プログラムつまり主要語彙集を作る。実装を省いたプログラムを作成するためには、課題を取り巻く現実世界を細分化する。

細分化するためには、課題に対する解の思い付きに対して次のような質問をする。

  • 本当にそうなのか?
  • どういう意味だ?
  • いつから、いつまで?
  • どこで?
  • 誰からの視点か、誰にとって該当するか?
  • どのようにして?
  • どんな状態なのか?
  • どうやって行うのか?
  • なぜなのか?
  • ほかの事例ではどうか?
  • これについては?
  • これだけか?
  • すべてそうなのか?
  • どうすべきだろうか?

それぞれの質問について、現時点で思いつくことのできた答えのアイディアや仮説をメモする。しかし、この答えは仮の目標であり、語彙集のネタである。

答えを思いつかなかった場合は、どんなことを調べれば答えられそうかを調べ、そのアイディアを書き込む。思いついた答えに問いを与え、それぞれの問いをさらに細分化する。

細分化した問いから主要語彙集へ
 

細分化した課題を取り巻く現実世界を、実装を省いた概略プログラムつまり主要語彙集に持っていくためには問いを捨てる。その基準としては、

  • 解を記述するのに最も重要な問いを中心に据える。
  • その課題を解決するために関連する問いや話題をピックアップする。
  • 使用できるライブラリや既存実装を調べ、利用できそうかどうか、語彙がうまく噛み合うかを検討する。
  • 粒度は適切か
主要語彙集からプログラムへ
 
  1. 簡単なトップレベルのみの動かないプログラムを作る。
  2. 語彙の不足があれば補う
  3. 使用している語彙に、短くて正確な名前を与える。
  4. 名前に適合する実装を与えるため、必要なブロックやコード片を付け加えて、メソッド・関数を作る。
  5. それぞれのメソッド・関数から不要な物を削り、実装を簡潔にする
  6. 長くなりすぎたメソッド・関数を、いくつかのブロックやコード片に整理し直し、可能ならそれらを別の下位レベルまたは同レベルの語彙として独立させる
  7. それらを再度精査する。以下、繰り返し。

分かりやすい表現で書く

語彙の用意とその定義記述を区別する

木下是雄氏は「事実と意見(判断)との区別を明確にすることが特に重要である」と述べている。木下氏の考えはおいておいて、語彙の選択とその定義記述際の注意点を紹介する。

用意する語彙の選択の注意点
 
  • 課題を取り巻く実世界に関してそのプログラムのなかで書く必要があるのは何かを十分に吟味する。
  • 抽象的であっても、どのような場合でも成り立つ明確な意味にする。
  • できるだけ明確な意味を持たせ、引数などのパラメタや子クラスなどの各実装側が責務を負えばよい詳細や具体性を混入させない。
語彙の定義記述の注意点
 
  • 語彙の意味を完全に記述する。
  • その定義に必要なレベルの粒度の語彙を使用して出来るだけ短く書く。
  • 詳細は他の語彙に頼るが、正確に使用する。
  • 語彙の意味に対して、勝手に前提条件を加えていないか注意する。
  • 先に考えた語彙の意味に問題があれば、語彙自体を見直す
さまざまな表現に対する指針

プログラムはさまざまな表現で成り立っている。その表現の書き方を次に紹介する。

変数
 
  • スコープはできるだけ短く。
  • 変更不要であれば変数ではなく定数にする
  • 同じ変数を複数の用途に使用しない
  • 原則的に変数の数は減らして簡潔にする。int a=0;a=hoge();return a;ではなくreturn hoge();
return
 
  • 関係ない場合はすぐにreturnする。if(hoge){長い長い実処理;;}returnではなくif(!hoge)return;長い長い実処理;;
ループ
 
  • ループは繰り返しではなく契約で考えて記述する。ループ事前条件、ループ中不変条件、ループ事後条件。
  • do-whileは原則使わない(賛否あり)
名前
 
  • 型の名前はそれがどういうものであるかを、値の名前は使い途を。
  • キャメルケースやスネークケースなどは、コーディング規約や言語の通例に従う。

プログラムを書く上でのポイントなど

最後に、プログラムを書く上でのさまざまなポイントをリストアップしておく。

  • 「動けばいい」という発想がどこかに混入していないか、常に疑いを持つ。

参考文献

引用・参照したWebページ


Creative Commons License
この作品は、クリエイティブ・コモンズ・ライセンスの下でライセンスされています。


※中盤辺りで順番がおかしいなどネタレベルの草稿なのであとで断りなく修正するかも知れません。