2019年02月11日

ソースコードが早く読めるようになる、シンプルコメント2 - リーダブル コード(58)

コメントを書くことで詳しく説明できるのは当然、人間なんだから自然言語で書かれた説明のほうがコードを読むよりも理解しやすいのは当然、と考える人がコードを書かない人に多いのですが、その考えは常識でもなんでもなく、むしろ逆です。

プログラムのソースコードに書く関数名や変数名は、英単語を省略しないようにするだけでも、かなり読みやすくなります。 たとえば、stat と省略しないで、status ときちんと書くことです。 それだけで、正しい単語に変換する人間の思考回路の動作が省略できますし、誤った単語を覚えてしまわないようになります。

そして、適切な関数名を付ければ、関数に対するコメントも必要なくなります。 コメントが必須となるコーディング・ルールの元で、コメントを書くと、関数名と同じ説明文になることから、コメントが必要ないことが体験できます。

開発中のコードは略語でも理解できるから問題なく、むしろ略語のほうが文字数が少なくて読みやすいことは実感しますが、略語が他の人や未来の自分が読めないコードの原因になっていることまで理解しておらず、その結果、略語で書かれた読めないコードが氾濫しています。 そのため、コードは読めないものだという誤った常識が形成され、コードは読めないものだからコメントを書かないと読めないと考えるようになってしまっています。しかし、そもそも略語を禁止して読めるコードにすればいいのです。

自然言語は曖昧であるため、自然言語(ワード)とコードの対応関係が必要になり、それが書かれていないと、コメントが書いてないコードよりももっと混乱することになります。 また、自然言語のワードとコードの対応関係の正しさの検証も必要になりますし、自然言語のコメントの対象としたコードのバージョン管理も必要になります。 自然言語では表記ゆれが発生しますし、ワードの意味が未定義のままになることもあります。

ですので、コメントが必要なら、必要最低限にしなければなりません。 最も効率よくコードの理解を助けるコメントがどういうものか、その研究結果をシンプルコメント2としてまとめました。


■ 関数名() のシンプルコメント

コメントより関数名のほうが、多くの人にレビューされて洗練されたフレーズになります。 関数を呼び出すときに関数名のコピーが書かれて見ることになるからです。1文字でも違うとエラーになります。

ただ、コメントが全く不要というわけではありません。 どうしても必要なコメントがあります。それは、関数の中の複数行のコードが意味のあるセンテンスになるとき、そのセンテンスの最初に書くコメントです。

それは、一般的に知られているブロック(センテンス)を開始するところに書くコメントです。

 /* XML を解析します */
 config.Flags =
   F_ParseXML2_Delegate |
   F_ParseXML2_OnStartElement;
 config.Delegate = work;
 config.OnStartElement = MkkwdsWorkClass_makeInFile_onXML_StartElement;
 e= ParseXML2( path, &config ); if(e){goto fin;}


上記のコメントは、その下の6行で行うソースコード内容を説明しています。

このセンテンスに対するコメントを省略すると、ソースコードを読む速度が下がります。 なぜなら、コメントが無いときは、たとえ全部でなくても、ソースコードを上から読んでいく必要があるからです。

そして、上記のコメントを更に洗練させたのが、関数名() のシンプルコメント2です。

 /* ParseXML2() */
 config.Flags =
   F_ParseXML2_Delegate |
   F_ParseXML2_OnStartElement;
 config.Delegate = work;
 config.OnStartElement = MkkwdsWorkClass_makeInFile_onXML_StartElement;

 e= ParseXML2( path, &config ); if(e){goto fin;}


コメントには関数名をそのまま書き、() を付けます。

前のバージョンのシンプルコメントでは、Call "ParseXML2" と書いていましたが、Call を書くと Call という単語が頭に入って混乱してしまう欠点がありました。 BASIC 言語で Call を書かなくなったことと同じ理由です。 () を書くことでその混乱が起きなくなると同時に、() の前が関数名であることが分かります。 () の前が関数名であることは、ほとんどのプログラミング言語の文法として採用されていて、命名規則の説明文でそれが関数名であることを説明するまでもないため、ルールがシンプルになります。

もし、コメントを自然言語で書くと、関数名による説明とコメントによる説明の2つの視点からの説明を受けることになります。 これは、知っている単語について毎回別の表現で言われることになるため混乱します。 たとえば、「printf します。」で済むことを、「コマンド プロンプトに出力します。 つまり、printf します。」と言われているようなことです。 脳内では printf というワードで処理内容をチャンキングして理解しているのに、チャンキングをばらした意味から読むことになるので、非効率なのです。 人によっては、違った概念でチャンキングが構成されていることもあり、その場合はコメントの内容を疑い始めることになり非効率です。

関数名はシンボルなので、シンボルから説明書を検索することができ、詳細を知ることもできるようになります。 関数を呼び出す側のコメントは、あまり洗練されていませんが、関数の説明書に書かれた文章は、何度も読み返されて洗練された文章になっていて、理解も早いです。 人によっては、関数の仕様を自分が最も理解しやすい表現で説明したメモを持っており、そちらを読み返した方が理解が早いです。

関数名とコードだけでは説明が不十分なときは、関数名() : 説明、というコメントにします。 説明には、なるべく変数名や関数名を含めるようにします。

 /* ParseXML2() : "in_TemplatePath" が指すファイルの XML を解析する */


関数を呼び出していないときや、呼び出している関数名が適切でないとき、かつ、後述する 変数名 = ... のシンプルコメントが使えないときに限り、自然言語でコメントを書きます。

また、リーダブル コード(56)で、空行をブロックの前ではなく読ませたい文の前に入れることで、重要な文を読ませることが自然にできることを説明しましたが、通常、シンプルコメントに書いた関数を呼び出す文が、読ませたい文になるため、その直前に空行を入れます。


■ 変数名 = ... のシンプルコメント

処理内容(関数名)の理解の次に重要なのが、その処理対象(引数や入力変数名)となるデータの理解です。 既存の変数やリテラルを引数にそのまま渡すときは、コメントがなくてもコードから読めますが、関数の前半で行う処理の出力を引数に渡すときは、前半の処理の出力データの説明が必要になります。

前半で行う処理のうち注目すべき出力変数(通常、引数に渡す変数)に関わる処理をまとめたセンテンス作り、それが開始するところに、変数名 = ... のシンプルコメントを書きます。 ... はコードの内容を指しています。

 /* path_written_in_other_XML = ... */
 GetApplicationStatus( &status ); /* status = . */
 e= ParseXML2_StatusClass_getAttribute(
   status, _T("path"), &path_written_in_other_XML ); if(e){goto fin;}


 /* ParseXML2() */
 config.Flags =
   F_ParseXML2_Delegate |
   F_ParseXML2_OnStartElement;
 config.Delegate = work;
 config.OnStartElement = MkkwdsWorkClass_makeInFile_onXML_StartElement;

 e= ParseXML2( path_written_in_other_XML, &config ); if(e){goto fin;}


path_written_in_other_XML 変数は、ParseXML2 関数の引数に渡す値の説明変数です。 リーダブル コード(57)で、空行の後に説明変数を積極的に使うことで、重要な文を英文として読めるようになり、理解しやすくなることを説明しましたが、変数名が長いのはそのためです。

説明変数は自然言語に近いため詳細で厳密な定義を知りたくなることがあります。 変数名 = ... のシンプルコメントが書いてあると、その定義を知ることができる場所がすぐに分かります。

説明変数を return 文に渡すことで、返り値の説明もできます。 ただし、Get 関数では、関数の説明書に書かれた洗練された文章のほうが適切かもしれません。

 /* path_written_in_other_XML = ... */
 GetApplicationStatus( &status ); /* status = . */
 e= ParseXML2_StatusClass_getAttribute(
   status, _T("path"), &path_written_in_other_XML ); if(e){goto fin;}

 return path_written_in_other_XML;


引数で出力する関数を呼び出したときは、関数呼び出しの後に、出力変数 = . のコメントを書きます。 上記では、status = . の部分です。 . は同じ行または前の行の文を指しています。 このコメントがあることで、変数の値が変化することが一目瞭然になります。 また、変数の詳細で厳密な定義を知りたいときに "変数名 =" で検索することができるようになります。 もしこのコメントがなければ、関数の引数が出力引数であることを覚えておくか関数の仕様書を見るか、実際に動作させなければ、変数の値が変化することが分からなくなり、コードを理解する効率が非常に悪くなります。

 GetApplicationStatus( &status ); /* status = . */


次回に続きます)

参考
>>> 空行はブロックの前ではなく読ませたい文の前に入れる - リーダブル コード(56)
>>> 空行の後は説明変数を積極的に使うこと - リーダブル コード(57)


sage_p at 01:20│Comments(0) プログラミング 

この記事にコメントする

名前:
URL:
  情報を記憶: 評価: 顔