教科書には載ってないプログラミングの小技
長年プログラマーをやってるといつの間にか身に付く小技。いつ誰に教えてもらったのか覚えてないけど、教科書にはあまり載ってないことも多いんですよね。
経験者にとっては当たり前のことかもしれませんが、そういう感じの小技をいくつか紹介します。
コード例はC#で書いていますが、大体どんなプログラミング言語でも考え方は同じです。
変数名はわかりやすく
int a = 200;
list1 = GetPrimaryList();
list2 = GetSecondaryList();
list2.Add(a);
b = list2.Sum();
うーん、なんだかよくわかりませんね。aとかbとか、list1とかlist2とか。
プログラムコードというのは、何度も読み返すものなので、「aって何だっけ?」ってなると無駄な脳のリソースを使ってしまいます。
int priceNew = 200;
primaryList = GetPrimaryList();
secondaryList = GetSecondaryList();
secondaryList.Add(priceNew);
toukei = secondaryList.Sum();
これならわかりやすいですよね。新しい価格(priceNew)が200円で、それを二次リスト(secondaryList)に追加して、その合計を統計値(toukei)として保持しておく。
変数名は多少長くなっても問題ありません。それよりは、可読性を重視したほうがメリットがあります。あと、最後のtoukeiはstatisticsとするとカッコイイですが、日本人のとってなじみの薄い英語を使うよりは、堂々とローマ字で「toukei」と書いちゃいましょう。少しダサいけど、可読性のほうが重要です。
プログラムコードは、それ自身がドキュメントにもなり得ます。変数名をわかりやすくして、とにかく可読性を上げていきましょう。
マジックナンバーを避ける
Puyo puyo[,] = new Puyo[6, 14];
for (int i = 0; i < 6; i++)
{
for (int j = 0; j < 14; j++)
{
puyo[i, j].Clear();
}
}
ここで出てくる「6」とか「14」というのが、いわゆるマジックナンバーというやつです。「謎の数字」という意味でマジックナンバーと呼ばれます。
こういう突然出てくる定数値は、何を表わしているのかわかりにくいです。また、この値を変更しなければならないとき、プログラムの全部の場所を変更しなければいけません。しかも、別の意味の6とか14があったとき、それも間違って変更してしまう危険性があります。
最初に定数を定義して、マジックナンバーはできるだけ避けましょう。
const int FIELD_WIDTH = 6;
const int FIELD_HEIGHT = 14;
Puyo puyo[,] = new Puyo[FIELD_WIDTH, FIELD_HEIGHT];
for (int i = 0; i < FIELD_WIDTH; i++)
{
for (int j = 0; j < FIELD_HEIGHT; j++)
{
puyo[i, j].Clear();
}
}
インデックスのj
ループのインデックス(カウンタ)には、よく「i」が使われます。indexとかiteratorとかのiです。で、ループが2重になってるとき、次のインデックスとして「j」が使われることが多いです。i, j, k, l, …という順番で使うこの慣習は、数学の慣習から来ていると思います。
ところがこのiとj、非常に見分けにくいです。フォントによっては、jの左下のくるんっと曲がってるところが無くて、棒の長さが違うだけの場合もあります。
なので、ループが2重以上になってるときは、わかりやすいループのインデックスを使いましょう。
const int FIELD_WIDTH = 6;
const int FIELD_HEIGHT = 14;
Puyo puyo[,] = new Puyo[FIELD_WIDTH, FIELD_HEIGHT];
for (int ix = 0; ix < FIELD_WIDTH; ix++)
{
for (int iy = 0; iy < FIELD_HEIGHT; iy++)
{
puyo[ix, iy].Clear();
}
}
インデントは浅く、アーリーリターン
if (userId >= 0)
{
if (rensa > 0)
{
if (x >= 0 && x < FIELD_WIDTH)
{
if (y >= 0 && y < FIELD_HEIGHT)
{
if (puyo[x, y] != null)
{
for (int iColor = 0; iColor < COLOR_NUM; iColor++)
{
if (puyo[x, y].Color == iColor)
{
puyo[x, y].Animation();
}
}
}
}
}
}
}
インデントがとんでもなく深いことになっています。横幅の狭いエディタだと、肝心のコードが画面の右端よりさらに先の彼方へ消え去ってしまいます。閉じ括弧がどれに対応してるのかもよくわかりません。
できるだけインデントを浅くするために、アーリーリターン(早期リターン)のクセをつけましょう。
if (userId < 0) return;
if (rensa <= 0) return;
if (x < 0 || x >= FIELD_WIDTH) return;
if (y < 0 || y >= FIELD_HEIGHT) return;
if (puyo[x, y] == null) return;
for (int iColor = 0; iColor < COLOR_NUM; iColor++)
{
if (puyo[x, y].Color != iColor) continue;
puyo[x, y].Animation();
}
ふう、見やすくなりました。アーリーリターンを使えるようにするためには、1つの機能が関数(メソッド)になってないといけません。複数の条件のせいでインデントが深くなりそうなところがあったら、関数として切り出しましょう。
ところで、最初の例は「条件に合致するなら」でifを書いていましたが、アーリーリターンの例は「条件に合致しないなら」でifを書いています。このとき、「AかつB」の否定は「(Aの否定)または(Bの否定)」になります。ド・モルガンの法則というやつですね。
forやforeachを抜けるときは、returnじゃなくてcontinueやbreakを使います。
同じコードは2度書かない
void Main()
{
puyo.Construct();
puyo.ReadFromDB();
if (puyo.Exists)
Console.WriteLine(puyo.Name);
tetris.Construct();
tetris.ReadFromDB();
if (tetris.Exists)
Console.WriteLine(tetris.Name);
magicalStone.Construct();
magicalStone.ReadFromDB();
if (magicalStone.Exists)
Console.WriteLine(magicalStone.Name);
}
なんか同じようなことを何度もやってるような気がします。もし、この一連の処理(Construct→ReadFromDB→WriteLine)に追加や修正があったらどうしましょう? オブジェクトの数だけ修正を加えるのは大変です。
それに、なんだかとっても、見にくいです。
同じコードを2回以上書くと、あとあと修正が大変になります。できるだけ、同じコードは2回以上書かないようにしましょう。
void Main()
{
CreateAndWrite(puyo);
CreateAndWrite(tetris);
CreateAndWrite(magicalStone);
}
void CreateAndWrite(Game game)
{
game.Construct();
game.ReadFromDB();
if (game.Exists)
Console.WriteLine(game.Name);
}
動いてる理由を把握する
いろいろプログラムを書いていると、「なぜ動いてるのかわからないけど、なんかうまくいった」ということがあると思います。しかしこれはとても危険です。
なぜうまく動いているのか、ちゃんと把握するようにしましょう。
プログラマーというのは、プログラムコードに対する最終責任者です。何か問題が発生したとき、他の人は助けてくれません。というか、助けられません。自分で書いたコードは自分の責任。常に、責任のあるコードを書くように心がけましょう。
小技を駆使して業務改善
どれもこれも、ほんとに小さな小技です。でも、こういういろいろな小技を駆使すると、脳のリソースを無駄に使うことなく、仕事のスピードがアップします。そうやってどんどん自分の業務改善をしていきましょう。
仕事がスピードアップして余った時間は、次の仕事をするもよし、寝るもよし。
ディスカッション
コメント一覧
まだ、コメントがありません