2012/05/26

美しいソースコードとは

ソースコードの自分なりの書き方まとめです。
まずは、「美しいソースコード」とは何であるかをこの場で定義しましょう。
人によって、その定義は千差万別ですし、論争の元にもなりますから…
加えて、場合によっても変わります。

以下はあくまでも私見ですが、
競技プログラミングにおいては無駄を最小限に留めた「効率」「短さ」が最優先
ショートコーディングなんかはすごいですね。

企業においては、保守管理、引き継ぎなども考慮して「可読性」「安全性」が最優先
社内で取り決めをしている企業もあります。
どちらにも言えることだと、インデント(tab)の取り方や中括弧({)の付け方でしょうか
if文一つでも3つ以上にわかれるようです。

さて、前置きはこの辺りでいいでしょう
自分も結構まちまちに書いてるので一概には言えませんが
この記事に置いては以下のように定義します。

美しいソースコード」 とは 「可読性を優位した効率の良いソースコード」である。

まあ、分かりやすく言えば、ちゃんと動いて綺麗なソースだね!ってことです。

あくまで自分なりですので、「ここはこうだろ」とか「これはいただけない」といったことがあれば、参考のために教えていただきたいです。
自分もまだまだ初心者ですのでおそらく間違ってることもあるでしょうから
あくまでもそういう例として、鵜呑みにする必要はないと思います。
ホントのことを言うと、この記事を書くにあたって書いたものを読み返すとまちまちだったので
結構独断と偏見が多いかも( ^ω^) ・・・


とりあえず、まずはインデント、プログラムの流れについてです。
以下のプログラムはAOJのLeap Year です。
>西暦 m 年から n 年までの間にあるすべてのうるう年を出力して終了するプログラム


#include

//うるう年判定
int leap(int n){
 return (n%4==0 && n%100!=0 || n%400==0)?1:0;
}


//うるう年出力
void putyear(int m,int n){
 int i,flag=0;

 for(i=m;i<=n;i++)
  if(leap(i)){
   printf("%d\n",i);
   if(!flag)
    flag=1;
  }

 if(flag == 0)
  printf("NA\n");
}


int main(){
 int m,n;

 while(scanf("%d %d",&m,&n) && m!=0 && n!=0){
  putyear(m,n);
  printf("\n");
 }

 return 0;
}


全体的に言うと、一番に気にすべきなのはタブを使ったインデントです。
こんなことを言うまでもないとは思いますけど一応… 
関数の内部を書くとき、if,for,while,などで1つタブを挿入ですね。指が勝手にやってくれます。
因みに私はタブ幅は4です。
この辺についてもエディタやフォントによって変わるので一概には言えません。
ですが、決してスペースなどではなくタブを使いましょう。

次に、if文を一行で書くといったこともあまりしてほしくはないです。
下記は動作は同じですし、2つとも同義です。

// 1行if文(・A・)イクナイ!!
if(hoge == 0) break;

if(hoge == 0)
 break;

1行だとやはり見づらいです。

関数上部のコメントも、この例程度なら別に付けなくてもいいですが、大きなコード、パッと見て何の処理かわからないコードであれば、
javaのドキュメントのように
/** 
 * うるう年か否かを判別
 * @param n 西暦
 * @return うるう年ならば1そうでなければ0
 */
int leap(int n){
 return (n%4==0 && n%100!=0 || n%400==0)?1:0;
}
のように書くとよりわかりやすいかもしれません。
IDEによっては読み込んでくれるものもあると思われます。
よく言いますが未来の自分は他人ですから、コメントを書いておくに越したことはないです
自分は、結構書きそこねて、面倒だった時がありました

次に、空行です。
全部ぴっちりくっつけたままだとやっぱり、見づらいです。

自分は全体として、以下のように分けています。

関数名
初期化部
    ~
   
主処理部
    ~
   
戻り値部

上記コード もそうしていますが、だいたい3ブロックくらいに分けると見やすいです。
各部の中でも長くなるときは空行を挟むといいですね。
各部の区切りは1行、関数同士の区切りは2行といったふうにするともっといいと思います。

後は、細かいところばかりですが、この細かいところがなかなか難しい

//変数への代入の時の空白
int flag = 0,i;

int flag=0,i;


//for文での空白
for(i = m;i< = n;i++)

for(i=m;i<=n;i++)


//if文での空白
if(flag==0)

if(flag == 0)


//if文や関数などの中括弧の付け方
//開いてる
if(hoge)
{
 ~
}
else
{
 ~
}

//半々
if(hoge){
 ~
}
else{
 ~
}

//くっついてる
if(hoge){
 ~
}else{
 ~
}

※コメントアウトすべきですが比較のためしていません。

このようにスペース一つでだいぶ雰囲気が変わりますね。
for文の中はいつも詰めてますが、if文の中はまちまちですが、&&や||の両側は開けるようにしています。

そして、下半分の中括弧ですが大体の人は上記3パターンにわかれるようです。
自分はくっついてるやつで書いてます。
友人は }else{ が飛行生物に見えるとかなんとか

と、基礎の基礎はだいたいこんな感じでしょうか
いざまとめるとできないものですね
クラス、関数、メソッドの命名規則や習慣についてもまとめたいです

0 件のコメント:

コメントを投稿