おもしろwebサービス開発日記

Ruby や Rails を中心に、web技術について書いています

プログラミング作法 第一章「スタイル」

最近、効率のよい○○にはまっています。

  • 効率のよいコードの書き方
  • 効率のよいテストの仕方
  • 効率のよいDBの設計

などなど。
この中から、まず手始めに効率の良いコードの書き方を学ぶことにしました。個人的に、小手先のテクニックを学ぶよりも基礎から勉強した方が長い目で見た時に効率がいい!と思うので、思いっきり基礎から勉強していきます。そこで、基礎からの勉強のためにプログラミング作法と言う本を買ってみました。

プログラミング作法

プログラミング作法

ざっと読んだ感じ、ただ読んだだけだと頭に残らなそうなので、個人的に気になったところについてブログでまとめていこうと思います。


最近いろんなことに手を出しているので、全部読みきるまでには結構かかりそうですが…気長にお付き合いください。まずは第一章。

名前

グローバルにはわかりやすい名前を、ローカルには短い名前を

ローカルグローバルにかかわらず、長くてもわかりやすい名前をつけた方がよいと思っていた。

関数には能動的な名前をつける。名前は動詞+名詞の順番がよい。(getTime()とか)

time_displayみたいな名前つけてたかも><。統一しよう。

式と文

自然な形の式を使おう。式は自分で音読するつもりで書こう。条件式に否定が含まれていると、間違いなく分かりづらくなる。

unlessはいいのかな?railsで結構多用するんだけど。こんな感じで。

name = params[:name] unless params[:name].blank?
複雑な式は分割しよう

式を分割してコードが複数行になった時に、途中結果を変数に格納しなくてはいけなくなって、変数名が増えて書きにくい→短くしちゃえ
みたいなことをしがちなので、これから自重しようと思います。

一貫性と慣用句

getsは絶対に使ってはならない。getsで読み込まれる入力の量を制限する手段がないからだ。この点はセキュリティの問題を引き起こす。*1

そうだっけ?やばい。Cだいぶ忘れてるなー。

多分岐の判定にはelse-ifを使おう

else-ifを使うっていうだけなら、「当たり前じゃん」って思うんだけど。

間違い例

if (argc == 3)
  if ((fin = fopen(argv[1], "r")) != NULL)
    if ((fout = fopen(argv[2], "w")) != NULL) {
      while ((c = getc(fin)) != EOF)
        putc(c, fout);
      fclose(fin); fclose(fout);
    } else
      printf("Can't open output file %s\n", argv[2]);
  else
    printf("Can't optn input file %s\n", argv[1]);
else
  printf("Usage: cp inputfile outputfile\n");

正解例

if (argc != 3)
  printf("Usage: cp inputfile outputfile\n");
else if ((fin = fopen(argv[1], "r")) == NULL)
  printf("Can't optn input file %s\n", argv[1]);
else if ((fout = fopen(argv[2], "w")) == NULL) {
  printf("Can't open output file %s\n", argv[2]);
  fclose(fin);
else {
  while ((c = getc(fin)) != EOF)
    putc(c, fout);
    fclose(fin);
    fclose(fout);	
}

僕は正常系を先に書いて異常系を後に書くってことを意識している(気がする)ので、その感覚で書くと間違い例のようになってしまいそうな気がした。(まあさすがにこの例では正解例を書くと思うけど)

個々の判定とそれに対応する動作はできるだけ近くに記述するのが原則だ。

って書いてあったけど、これは覚えておこうと思う。

関数マクロ

これはCオンリーな話なので飛ばす。

マジックナンバー

マジックナンバーとは、定数、配列サイズ、文字位置、変換ファクタその他、プログラム中に登場するリテラルな数値を指す。

数値はマクロではなく定数として定義しよう

Cで定数を定義するのって#defineするのが普通だと思ってた。enum文を使うのがいいらしい。

マジックナンバーまとめ
  • 2とか17とか、数値はそのまま使ってはだめ
  • 定数として定義するかライブラリを使うかなどすべき

ということらしい。

コメント

コードと矛盾させるな

これだけだと「そうだよねー」という話。でも

コメントはコードと一致しているだけではだめで、それに沿っていなければならない。

これは参考になった気がする。以下例。

time(&now);
strcpy(date, ctime(&now));
/* ctimeからコピーされた末尾の改行文字を除去 */
i = 0;
while(date[i] >= ' ') i++;
date[i] = 0;

コメントとコードは一致しているんだけど、コメントは改行文字について書いてあるのにコードは空白について書いてあるのが問題だよねーという話。これだと確かに一見コメントとコードが一致してない内容に見えてわかりづらい。

コメントまとめ

太字の部分を抜粋。

  • 当たり前のことはいちいち書くな
  • 関数とグローバルデータにコメントを
  • 悪いコードにコメントをつけるな。書き直せ。
  • あくまでも明解に、混乱を招くな

最後の部分を引用

コメントは、コード自体からは即座に意図を読み取れないプログラムの部分を解読しやすくするためのものだ。とにかくできるだけわかりやすいコードを各個と。そうすればそうするほど必要なコメントは少なくなる。優れたコードは下手なコードよりコメントが少なくて住むのだ。

コメントが少なくても分かるコードを書けるように頑張ろう。

感想

Cをだいぶ忘れている気がするので、ちょっとリハビリしたい。

*1:この本に書かれているサンプルプログラムは、ほとんどCです