869 Views
September 06, 24
スライド概要
プログラミングの世界では、技術的なスキルや効率性が重視されがちですが、実は「人に優しいコード」を書くことも非常に重要です。
ベガシステム技術勉強会の発表資料です。
ベガシステムは、創業1990年、30年以上続くIT企業です。 お客様との対話を大切にし、新たな価値を創造し続けます。
https://www.vega-net.co.jp/ 技術勉強会 人に優しいコードを書こう 2023年1月11日 株式会社ベガシステム
0.はじめに 古いネタですが... 全半角が混ざっていたり、タブ8だったり、0と””とNULLの区別が無かったり、 ネストが深かったりすると、プログラマを殺せると言われた時期がありました。 Copyright 2023 VEGA Systems Inc. All Rights Reserved. 1
0.はじめに
このC言語プログラムを実行すると、同じ結果が得られます。
左は面白いけど、読みにくいですよね。
世の中には、プログラムの読みにくさを競うコンテストがあります。
「The International Obfuscated C Code Contest」
#define P(X)j=write(1,X,1)
#define C 39
int M[5000]={2},*u=M,N[5000],R=22,a[4],l[]={0,-1,C-1,-1},m[]={1,-C,-1,C},*b=N,
*d=N,c,e,f,g,i,j,k,s;main(){for(M[i=C*R-1]=24;f|d>=b;){c=M[g=i];i=e;for(s=f=0;
s<4;s++)if((k=m[s]+g)>=0&&k<C*R&&l[s]!=k%C&&(!M[k]||!j&&c>=16!=M[k]>=16))a[f++
]=s;if(f){f=M[e=m[s=a[rand()/(1+2147483647/f)]]+g];j=j<f?f:j;f+=c&-16*!j;M[g]=
c|1<<s;M[*d++=e]=f|1<<(s+2)%4;}else e=d>b++?b[-1]:e;}P(" ");for(s=C;--s;P("_")
)P(" ");for(;P("\n"),R--;P("|"))for(e=C;e--;P("_ "+(*u++/8)%2))P("| "+(*u/4)%2
);}
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
|_
_ | _
|
_ _ | _ _ | _
_ | _ _ _ |_ _ _ _
|
| | _ |
| _ |_ _|_ _|_ _| _ _| | |
| |_| _| |_ _ _ _ |_ _ _ _ _ _| |_| | |_ | |
|_| _| _ _
| |_ _ | | |_|_ _| _| _ _ _ _ _ _| _ _ _ _ |_ | | | _|_|
| _| _| _| | | | _ _|_| | _ |_ _| _ _ _ _| _| |
|
| | _| | _|
|
| | _| _|
| |_ _| _ _ _| | |_ _ | _ |_
_| _| | | |_ | | _| | _| |
| |_ _|_ |_| _ _|
_ _|_ _ | | _ |_ |_ _ | | | | | | | |_ |_ _| | |
|_ | |
|_ | | _ _|_ _ _ | _| |_ |_ |_ |_ _|_ | | |_ _ | _ _ |_ |
| _| _|_ | | | | _ _ | |_ _ _ _|
| | | _| _ _ |_|_ | | |_ _ _|_
|
|
| |
|_| | |_ _|
|_ |_
_| | | |_ | |_ _|
|_ | _|_ _ _ _ _ _ _| |
| |_ _| |_ | |
_ _|_ _| | _ _ _| | | _|_ | _| | | | |_ |
|_ | _|
|_| _| | _ | |_ _ _ _|_ | _ | | |_ _|_ |_ _| |_ _ _| | |_ _ _| | |
| _| _| | _| |_ |_ | |
| |_ | | |_ | | _ _| | _| | _ _|_ _ _ _ _| |
|_ | |_ _| | |_ _ | | | | |_ _| _| |_ |_|_ _| _|_ _ | _|_ _
|
|
|
| _| | | | | | | | |_ _ | |_ | | |_ _ _ _| | _ |_ _ _ _ | |_ _|_ _| |
| | _| |_| | | | |_ _|_
|_| | _|_ _|_
_ |_ |_ _|
|
| | |_ _ _ _ _|
| |_ | _ _|_ _|_ |
_|_ _ _|_ _
_ |_ _|_ _ | _ _|_ _|_ _ _ _ _ _ _ |
| | | | _ | _ _|_| | _ _ _ _ _ | |_ _ _ _ | _ |_ | _| _ _ | |
|
| |_|_ _| |_ _ _ _ _|_ |_ _ _| _ _| | _| |
| | | _| _|_ | |
|_| |
|_| | | _ | | _ _ |
| |
|_ | _ _|_ _ _| |_ _| | | | _ | |_ _| | _|
| _|_ _ _| |_ _|_ | | |_| | |_ _ _|_ _| | |_ _ _| | |_ _ |_ _| _ _|_ |
|
| | _ _ _ _ _ _| | |
| |_ _ | _ _|_| |_ _ _ | |_ |_ | _ _| _ _ _|
|_|_ _|_ _ _ _ _ _ _ _|_ _|_|_ _ _ _|_ _ _ _ _|_ _ _ _ _|_ _ _ _ _ _ _ _ _ _ |
#define P(X)j = write(1,X,1)
#define C 39
int M[5000] = {2};
int *u = M;
int N[5000];
int R = 22;
int a[4];
int l[] = {0, -1, C-1, -1};
int m[] = {1, -C, -1, C};
int *b = N;
int *d = N;
int c, e, f, g, i, j, k, s;
main()
{
for(M[i=(C*R)-1] = 24; f|d >= b; )
{
c = M[g=i];
i = e;
for(s = f = 0; s < 4; s++)
{
if((k = m[s]+g) >= 0 &&
k < C*R &&
l[s] != k % C &&
(!M[k] || !j && c >= 16 != M[k] >= 16))
{
a[f++] = s;
}
}
if(f)
{
f = M[e = m[s = a[rand() / (1+2147483647/f)]] + g];
j = j < f ? f : j;
f += c &-16 * !j;
M[g] = c | 1 << s;
M[*d++ = e] = f | 1 << (s+2) % 4;
} else {
e = d > b++ ? b[-1] : e;
}
}
P(" ");
for(s=C; --s; P("_"))
{
P(" ");
}
for( ; P("\n"), R--; P("|"))
{
for(e=C;e--;P("_ "+(*u++/8)%2))
{
P("| "+(*u/4) % 2);
}
}
}
Copyright 2023 VEGA Systems Inc. All Rights Reserved.
2
0.はじめに 人に優しいコード、 数日後、数週間後の自分が理解できるコード、 書けていますか? 今回のテーマは「人に優しいコードを書こう」です。 理解しやすい・可読性の良いコードの書き方 を考え直せればと思います。 ➢ 良い名前をつける ➢ 適切なコメントを書く ➢ 意味のある単位に分割する ➢ キレイに整形する Copyright 2023 VEGA Systems Inc. All Rights Reserved. 3
■目次 1. 表面上の改善 1. 2. 3. 名前に情報を詰め込む 誤解されない名前 美しさ 2. ループとロジックの単純化 1. 2. 制御フローを読みやすくする 巨大な式を分割する Copyright 2023 VEGA Systems Inc. All Rights Reserved. 4
1.1. 名前に情報を詰め込む プログラムに使われる名前は、主に次の5つの鉄則を守る必要がある。 1. 2. 3. 4. 5. 明確な単語を選ぶ 汎用的な名前を避ける 抽象的な名前よりも具体的な名前を使う 接尾辞や接頭辞を使って情報を追加する 名前の長さを決める Copyright 2023 VEGA Systems Inc. All Rights Reserved. 5
1.1. 名前に情報を詰め込む 1. 明確な単語を選ぶ class Thread { void Stop(); }; これでも良いが、動作に合わせてもっと明確な名前にした方が良い。 ・取消ができない重い操作なら Kill() ・後から再開 Resume() できるなら、一時停止 Pause() 2. 汎用的な名前を避ける def func1(value): return value ** 2 valueの2乗の値を示しているので、 関数名は func1 ではなく square などとした方が良い。 3. 抽象的な名前よりも具体的な名前を使う 任意のTCP/IPポートをサーバがリッスンできるかを確認する ServerCanStart() という名前のメソッドがあったとする。 より具体的な名前としては CanListenOnPort() とした方が良い。 Copyright 2023 VEGA Systems Inc. All Rights Reserved. 6
1.1. 名前に情報を詰め込む
4. 接尾辞や接頭辞を使って情報を追加する
時間やバイト数のように計測できる値であれば変数名に
単位を入れた方が良い。
関数の仮引数
単位を追加した仮引数
Start(int delay)
delay → delay_secs
CreateCache(int size)
size → size_mb
ThrottleDownload(float limit)
limit → max_kbps
Rotate(float angle)
angle → degrees_cw
5. 名前の長さを決める
変数や関数の名前は「長い名前を避ける」という暗黙の了解がある。
これを真に受けると、1つの単語や1文字だけの名前になってしまう
場合があるが、スコープが小さければ短い名前でも構わない。
if (debug) {
map<string, int> m;
LookUpNamesNumbers(&m);
Print(m);
}
Copyright 2023 VEGA Systems Inc. All Rights Reserved.
7
1.2. 誤解されない名前 データベースの問い合わせ結果を処理するコードを書いているとする。 results = Database.all_objects.filter(“year <= 2011”) filterがあいまいな言葉であるため、 resultsがどちらを指しているのか分からない。 ・「year <= 2011」のオブジェクト ・「year <= 2011」ではないオブジェクト 「選択する」のであれば select() 「除外する」のであれば exclude() にした方が良い。 Copyright 2023 VEGA Systems Inc. All Rights Reserved. 8
1.3. 美しさ
#define P(X)j=write(1,X,1)
#define C 39
int M[5000]={2},*u=M,N[5000],R=22,a[4],l[]={0,-1,C-1,-1},m[]={1,-C,-1,C},*b=N,
*d=N,c,e,f,g,i,j,k,s;main(){for(M[i=C*R-1]=24;f|d>=b;){c=M[g=i];i=e;for(s=f=0;
s<4;s++)if((k=m[s]+g)>=0&&k<C*R&&l[s]!=k%C&&(!M[k]||!j&&c>=16!=M[k]>=16))a[f++
]=s;if(f){f=M[e=m[s=a[rand()/(1+2147483647/f)]]+g];j=j<f?f:j;f+=c&-16*!j;M[g]=
c|1<<s;M[*d++=e]=f|1<<(s+2)%4;}else e=d>b++?b[-1]:e;}P(" ");for(s=C;--s;P("_")
)P(" ");for(;P("\n"),R--;P("|"))for(e=C;e--;P("_ "+(*u++/8)%2))P("| "+(*u/4)%2
);}
IOCCC日本語ネタバレ解説
https://mame.github.io/ioccc-ja-spoilers/
Copyright 2023 VEGA Systems Inc. All Rights Reserved.
9
2.1.制御フローを読みやすくする 条件やループなどの制御フローはできるだけ「自然」にする。 コードの読み手が立ち止まったり読み返したりしないように書く。 どちらの方が読みやすいか? ・if (length <= 10) ・if (10 >= length) 最初の方が読みやすい理由 左側 右側 「調査対象」の式。変化する。 「比較対象」の式。あまり変化しない。 ■質疑応答で出た例 if (NULL == a) if (NULL = a) コンパイルエラー if (a == NULL) if (a = NULL) if (0 <= length && length <= 10) 0 <= length <= 10 Copyright 2023 VEGA Systems Inc. All Rights Reserved. 10
2.1.制御フローを読みやすくする
三項演算子も使いどころに注意が必要。
○前者の方が読みやすい。後者は長くて冗長に感じる。
time_str += (hour >= 12) ? “pm” : “am”;
if (hour >= 12) {
time_str += “pm”;
} else {
time_str += “am”;
}
×無理やり1行に納められていて、読みづらい。
return exponent >= 0 ? Mantissa * (1 << exponent) : Mantissa / (1 << -exponent);
こうすると、多少マシになるけど。
return exponent >= 0 ?
Mantissa * (1 << exponent) :
Mantissa / (1 << -exponent);
Copyright 2023 VEGA Systems Inc. All Rights Reserved.
11
2.1.制御フローを読みやすくする 変数のことが見える行数をできるだけ減らすと良い。 ➢ 関数から早く返す public Boolean Contains(String str, String substr) { if (str == null || substr == null) return false; if (substr.equals(“”)) return true; ... } ➢ ネストを浅くする 右の方がネストが浅くて読みやすい。 if (user_result == SUCCESS) { if (permission_result != SUCCESS) { reply.WriteErrors(permission_result); reply.Done(); return; } reply.WriteErrors(“”); } else { reply.WriteErrors(user_result); } reply.Done(); if (user_result != SUCCESS) { reply.WriteErrors(user_result); reply.Done(); return; } if (permission_result != SUCCESS) { reply.WriteErrors(permission_result); reply.Done(); return; } reply.WriteErrors(“”); reply.Done(); Copyright 2023 VEGA Systems Inc. All Rights Reserved. 12
2.2.巨大な式を分割する var update_highlight = function (message_num) { if ($("#vote_value" + message_num).html() === "Up") { $("#thumbs_up" + message_num).addClass("highlighted"); $("#thumbs_down" + message_num).removeClass("highlighted"); } else if ($("#vote_value" + message_num).html() === "Down") { $("#thumbs_up" + message_num).removeClass("highlighted"); $("#thumbs_down" + message_num).addClass("highlighted"); } else { $("#thumbs_up" + message_num).removeClass("highighted"); $("#thumbs_down" + message_num).removeClass("highlighted"); } }; var update_highlight = function (message_num) { var thumbs_up = $("#thumbs_up" + message_num); var thumbs_down = $("#thumbs_down" + message_num); var vote_value = $("#vote_value" + message_num).html(); var hi = "highlighted"; }; if (vote_value === "Up") { thumbs_up.addClass(hi); thumbs_down.removeClass(hi); } else if (vote_value === "Down") { thumbs_up.removeClass(hi); thumbs_down.addClass(hi); } else { thumbs_up.removeClass(hi); thumbs_down.removeClass(hi); } Copyright 2023 VEGA Systems Inc. All Rights Reserved. 同じ式を要約変数として関数 の最上部に抽出すると良い。 13
エンジニアは絶対読みましょう。 英語でも大丈夫!って人なら無料で読めます。 https://mcusoft.files.wordpress.com/2015/04/the-art-of-readable-code.pdf Copyright 2023 VEGA Systems Inc. All Rights Reserved. 14