コメントは正確で簡潔に
どうやったらコメントを正確で簡潔に書けるのかをまとめる
コメントを簡潔に
コメントは簡潔に書くことで、コードの可読性が向上する。以下の例では、冗長な説明を1行にまとめることで、より理解しやすくなる。
// intは CategoryType
// pairは最初のfloatは`score`
// 2つ名は`weight`
typedef hash_map<int, pair<float, float> > ScoreMap;
これなら1行に短縮して説明できる
// CategoryType -> (score, weight)
typedef hash_map<int, pair<float, float> > ScoreMap;
コメントは必要最小限の情報を簡潔に伝えることが重要だ。
曖昧な指示語は使わない
「これ、それ、あれ」などの指示語をコメント内に入れてしまうと、読み手は一度脳内で変換する必要がある。
// データをキャッシュに入れる。ただし、先にそのサイズをチェックする。
function saveDataCache(data){
}
この場合、「そのサイズ」は「データのサイズ」なのか「キャッシュのサイズ」なのかがコード内を読み進めなければわからない。
歯切れのいい文章にする
コメントは明確で簡潔な文章で書くことが重要だ。以下の例では、より直感的で理解しやすい表現に改善されている。
// これまでにスクロールしたURLかどうかによって優先順位を変える。
この文章でも問題ないが、以下の文章の方が単純で短く直感的になる。
// これまでスクロールしてないURL -> 優先度を高くする
コメントは簡潔で明確な表現を心がけることで、コードの意図がより伝わりやすくなる。
関数動作を正確に記述
関数の動作を正確に記述することは、コードの理解を深めるために重要だ。以下の例では、曖昧な表現を具体的な実装に基づいた説明に改善している。
// このファイルに含まれている行数を数える
function countLines(filePath){
}
この「行」にはいくつかの意味があり、複数の挙動が想像できてしまう。
- ""(空のファイル)は 0行?1行?
- "hello"は 0行?1行?
- "hello\n"は 1行?2行?
- "hello\n world"は 1行?2行?
- "hello\n\r cruel\n world\r"は 2行?3行?4行?
もっとも単純な実装は改行コードの「\n」を数えるUnixの「wc」コマンドと同じ実装だ。
// このファイルに含まれている「\n」を数える
function countLines(filePath){
}
これによって、改行がないファイルは0行とカウントされ、キャリッジリターンは無視されることもわかる。
- ""(空のファイル)は [0行]
?1行? - "hello"は [0行]
?1行? - "hello\n"は [1行]
?2行? - "hello\n world"は [1行]
?2行? - "hello\n\r cruel\n world\r"は [2行]
?3行?4行?
関数の動作を正確に記述することで、実装の意図が明確になり、予期せぬ動作を防ぐことができる。
出入力のコーナーケースに実例を入れる
関数の入出力のコーナーケースを実例で示すことは、関数の動作を理解する上で非常に重要だ。以下の例では、曖昧な説明を具体的な実例を含む説明に改善している。
// 'src'の先頭や末尾にある'chars'を除去する。
function strip(src: string, chars: string): string
{
}
このコメントだとあまり正確ではなく、以下のような疑問が生じる。
-
chars
は除去する文字列なのか、順序のない文字集合なのか? -
src
の末尾に複数のchars
があったらどうなるのか?
// 'src'の先頭や末尾にある'chars'を除去する。
// 例 : strip("ab123ba45a", "ab") -> "12345a"
function strip(src: string, chars: string): string
{
}
このコメントは、strip()
の機能をすべて示している。
実例を含めることで、関数の動作がより明確になり、期待される結果が理解しやすくなる。
コードの意図を書く
コードの意図を明確に記述することは、コードの理解を深めるために重要だ。以下の例では、単なるコードの説明から、より高レベルの意図を説明するコメントに改善している。
void DisplayProducts( List<Product> product ){
products.sort( COMPARE_PRODUCT_BY_PRICE );
// listを逆順にイテレートする
for ( list<Product>::reverse_interator it = products.rbegin(); it != products.rend(); ++it)
DisplayPrice(it->price);
...
}
このコメントは直下のコードを直接説明しているだけにすぎない。
void DisplayProducts( List<Product> product ){
products.sort( COMPARE_PRODUCT_BY_PRICE );
// 価格の高い順に表示する
for ( list<Product>::reverse_interator it = products.rbegin(); it != products.rend(); ++it)
DisplayPrice(it->price);
...
}
このコメントはプログラムの動作について高レベルから説明している。
またこれにより、DisplayPrice(it->price);
でソートしながら表示するのにも関わらず、直前のproducts.sort( COMPARE_PRODUCT_BY_PRICE );
でソートしている。
コメントを的確にすることで、冗長検査にもなる。
「引数に名前」をつけるコメント
例えばこんな関数呼び出しがあったとする。
connect(url, 1000, false);
URLと数値とプール値が渡されているが、なんの値か定かではない。
Pythonのような言語であれば引数を名前付きで渡すことができる。
def connect(rpc, timeout, use_encryption):
# ....
# 名前付き引数を使って呼び出し
connect(rpcURL = url, timeout = 1000, use_encryption = false):
ただ、JavaやC++のような言語ではできない。
そこでインラインコメントを使って同じような効果を生み出せる。
static void connect(String rpcURL, int timeout, boolean use_encryption){
// ...
}
// 名前付き引数を使って呼び出し
String rpcURL = "https://api.mainnet-beta.solana.com/";
connect( /* rpcURL = */rpcURL, /* timeout_ms = */1000, /* use_encryption = */false);
このようにして、「引数に名前」をつけるコメントが出来上がる。
connect( ... , false /* use_encryption */);
connect( ... , false /* = use_encryption */);
とやってしまうと、紛らわしくなってしまうので、値の前に置くのが大切だ。
情報を詰め込んだ言葉を使う
コメントでは、情報を効率的に伝えるために、適切な専門用語や表現を使用することが重要だ。以下の例では、冗長な説明を簡潔で情報量の多い表現に改善している。
// 所在地から余分な空白を除去する。また「Avenue」を「Ave.」にするなのどの、スリーレターコードに整形。
// こうすれば、表記がわずかに違う所在地でも同じものであると判別可能。
このコメントは以下のように情報を詰め込める。
// 所在地をスリーレターコードに正規化する ( 例: "Avenue" -> "Ave." )
「正規化」のように多くの意味を含んだ言葉や表現が数多く存在する。例えば、
- 「正規化」: データを標準的な形式に変換すること
- 「キャッシュ」: 頻繁にアクセスするデータを高速に取得するための仕組み
- 「バリデーション」: データの妥当性を検証すること
適切な専門用語を使用することで、より効率的に情報を伝えることができる。
まとめ
できるだけお小さな領域にできるだけ多くの情報を詰め込んだコメントを書くことは大切。
だが、
- 複数のものを指す可能性がある「それ」や「これ」などの代名詞は避ける。
- 関数動作はできるだけ正確に
- コメントに含める出入力は実例は慎重に
- コードの意図は
詳細レベル<高レベル
- 情報を詰め込んだ言葉を使う
ことがもっとも大切となる。
参考文献
- 『リーダブルコード』(Dustin Boswell, Trevor Foucher 著、角征典 訳、オライリー・ジャパン、2012年)