いろんなものはつながっている

コードは理解しやすくなければいけない

以前から読もうと思っていたリーダブルコードを読んだ。訳者まえがきにあるように、本書は面白い。アマゾンなどのレビューに、目新しいことはない、といった内容が書かれているがそんなの関係ない。本書は確かに面白い。

コードは理解しやすくなければいけない

読み終わった感想は、最初にかかれているとおり、コードは理解しやすくなければいけない、これにつきる。

もうちょっとかみくだいていうと

コードは他人が最短時間で理解できるように書かなければいけない。

ここでいう他人とは、コードを書いた本人も含まれる。以前の職場では、3か月後の自分は赤の他人ということがいわれていたが、まさにその通りだと思う。

短ければいいってものではない
本書から引用すると

assert((!bucket = FindBucket(key))) || !bucket->IsOccupied());

より

bucket = FindBucket(key);
if(bucket != NULL) assert(!bucket->IsOccupied());

のほうがわかりやすいでしょ、ということ。

名前に情報をつめこむ

変数や関数の名前をから何の役割を担うものか誤解なくわかる必要がある。これは意識してわかりやすい名前をつけるということから始まると思う。これが簡単そうですごく難しい。まず英語のボキャブラリが貧弱だからどうしても同じような単語になってしまう。シソーラス辞典を利用してボキャブラリを増やしていくしかないか。

インデックスにもっと明確な名前をつける
ループインデックスをi,j,kといった無機質な変数よりbox_i, name_iといったものにしたほうがみやすいとある。確かにそうだ。

for(int i = 0; i < 10; i++){
    for(int j = 0; j < 20; j++)
        stuff[i*class_num + j] = ....
    }
}

より

for(int class_i = 0; class_i < 10; class_i++){
    for(int users_i = 0; users_i < 20; users_i++)
        stuff[class_i*class_num + users_i] = ....
    }
}

のほうがわかりやすい。特にfor文のなかでループインデックスを使う場合は名前を具体的にしたほうが読みやすい。
また、自分はたまに以下のように違うイテレータをインクリメントしてしまうことがあるのでこれは意識して取り入れてみよう。

for(int i = 0; i < 10; i++){
    for(int j = 0; j < 20; ”i++”)
        ....
    }
}

名前に単位の情報を追加する
delayやsizeといった量には単位がつきものでコメントにかくぐらいなら変数名に加えてしまえばいい。確かにそうだ。sizeではなくsize_mbとか、period_msとか。

size, lengthでは抽象的すぎる
これもよくまよう。特にsizeという変数を自分でかいておいて、文字数なのかバイト数なのか読み直してときに迷うときがある。だから最初からsize_byteとか。

範囲指定の際のmin/min,first/last,begin/endの使い分け
・最大、最小の限界値を含める場合はmin,max
・範囲を指定し、最後の要素も範囲に含める場合はfirst,last。最後の要素を含めない場合はbegin,end

意図や気づきをコメントに残す

いろいろ注意事項はあるがまずは

コードを理解するのに役立つものなら何でもいいから書こう

というのが著者からのアドバイス。

とにかく書き始めてみよう
コメントを書く作業は3つの簡単な手順に分解できる。
・頭のなかにあるコメントをとにかく書き出す
・コメントをよんで改善が必要なものをみつける
・改善する

とはいうものの、実際にコメントを書こうとするとなかなか難しい。それはそもそも自分のコードが不必要に複雑なためかもしれない。

コメントのためのコメントをしない
たとえば、

// profitに新しい値を設定する
void SetProfit(double profit)

// 与えられたsubtreeに含まれるnameとdepthに合致したNodeを見つける
Node* FindNodeInSubtree(Node* subtree, string name, int depth);

はコメントで新しい情報を提供していない
後者を例にとると意味のあるコメントとは以下のようなもの。

// 与えられた'name'に合致したNodeからNULLを返す。
// もしdepth <= 0 ならば 'subtree'だけを調べる
// もしdepth == Nならば、'subtree'とその下のN階層まで調べる
Node* FindNodeInSubtree(Node* subtree, string name, int depth);

定数にコメントをつける
なぜその定数にしているかを記述したほうがよい。

// 合理的な限界値。人間はこんなに読めない
const int MAX_RSS_SUBCRIPTIONS = 1000;

読み手が??と思うだろうと思うところにはコメントをつける
読み手が??と思うということはそもそも自分のコードが不必要に複雑なのかもしれない。それでもとりあえずは、自分の頭の中にある考えをはきだしておくこと。

コメントは領域に対する情報の比率が高くなければいけない
コメントは簡潔にかく

// intはCategoryType
// pairの最初のfloatは'score'、2つめは'weight'
typedef hash_map<int, pair<float, float> > ScoreMap;

// CategoryType->(score, weith)

あいまいな代名詞をさける
多少冗長になっても代名詞はさけたほうがいい。

//データをキャッシュにいれる。ただし、先にそのサイズをチェックする。
そのが、データかキャッシュかあいまい

→ 

データをキャッシュに入れる。ただし、先にデータのサイズをチェックする。

関数の動作を正確に記述する

// このファイルに含まれる行数を返す
int CountLine(string filename){...}

// このファイルに含まれる改行文字('\n')を数える

入出力のコーナーケースを実例に選んでコメントに残す

// 'src'の先頭や末尾にある'chars'を除去する
String Strip(String src, String chars){...}

charsは除去する文字列なのか順不同の文字なのか。
繰り返しがあったらどうするのか等々

//実例:Strip("abba/a/ba", "ab")は"/a/"をかえす
String Strip(String src, String chars){...}

もちろん実例は意味のあるものでないといけない。 Strip(“ab”, “b”)はbをかえすでは意味がない。

情報密度の高い言葉を使う
ヒューリスティック
ブルートフォース
ナイーブソリューション

制御フローの読みやすさ

do/whileループを避ける
do/whileループはたいていwhileループで書き直せることが多い。

関連記事

コメント

  1. この記事へのコメントはありません。

  1. この記事へのトラックバックはありません。

スポンサード リンク

カテゴリー

スポンサード リンク