2.2 関数化

2.2
関数化
関数と上手く付き合う
関数化の意義
1. 何度も同じ処理を書かなくて済む
►修正し忘れの防止
2. 処理のカタマリに名前を付けることが出来る
►パッと見似てるけど違う処理の区別
►コードの文脈が伝わりやすくなる
3. コードが短くなり、見通しが良くなる
►言うまでもなくダラダラと長いより見やすい
2.2 関数化
2
1.何度も書かなくて済む
最初に書く時はコピペで済んでしまう
►問題は修正するとき
►例えば以下のコードに赤字部分を追加するときは?
// このコードは全部で数千行ある中の至る所にコピペしてある
// ちなみに弾の追加コードである
for (int i = 0; i < BULLETS_LIMIT; i++) {
if (bullets[i].enabled) continue;
bullets[i].location = player.location;
bullets[i].velocity =
VAdd(VGet(0, -BULLET_SPEED, 0), player.velocity);
bullets[i].enabled = TRUE;
break;
何箇所あるか分からないから
追加し忘れる可能性が高い
}
2.2 関数化
3
関数化すれば解決
AddBullet 関数にさっきのコードをまとめると
►使う時は AddBullet();と書くだけ
►修正する時は AddBullet 関数の中を修正するだけ
これで修正し忘れる心配は無くなった
2.2 関数化
4
2.処理に名前を付けられる
ただ中身を入れ替えるだけの3行のコード
►大量のコードに混ざると何なのか分からない
...
int temp = a;
a = b;
b = temp;
...
でも swap 関数として作って呼び出すと
►何がしたいのか一目瞭然
►ついでに不要な変数が減った
...
swap(&a, &b);
...
2.2 関数化
5
処理に名前を付ける意義
処理に名前を付けると文脈が明らかになる
►本来必要なコメントが不要になる
本当に美しいコードにはコメントが不要
►読めば分かるということは質問される側も楽
関数には良い名前を付けるように心掛けよう
►悪い名前では逆に読みにくくなってしまう
(もし swap 関数の名前が tarako だったら・・・?)
2.2 関数化
6
3.コードが短くなる
これはあえて例を示すまでもなく・・・
►一度定義してしまえば呼び出しは1行で済む
ただそれだけのことだね
2.2 関数化
7
上手な関数化のコツ
経験的な分野なので説明が大変難しい
►みんなも色々試してコツを掴んでほしい
出来る限りの考え方を解説する
►パキッと分かるモノではない前提で聞いてね
2.2 関数化
8
「歩く」関数を考える(1)
単純な概念なのに中身が複雑なら関数化の目安
►例えば人間が「歩く」という処理
// 10歩前に歩く処理
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) foot = 右足;
else
foot = 左足;
footを上げる;
footを前に出す;
footを下ろす;
}
※C風の擬似言語だよ!
歩くとき毎回こんな処理書いてられない
►ならば関数化しよう
2.2 関数化
9
「歩く」関数を考える(2)
まずはこんな関数になった
►中身はさっきのコピペ
// 10歩前に歩く関数
void walk10steps() {
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) foot = 右足;
else
foot = 左足;
この部分はコピペ
footを上げる;
footを前に出す;
footを下ろす;
}
}
10歩以外の時はどうする・・・?
2.2 関数化
10
「歩く」関数を考える(3)
引数に歩数を渡すように変えてみた
►それに対応してループ条件が変わったね
// 指定された歩数だけ前に歩く関数
void walk(int steps) {
for (int i = 0; i < steps; i++) {
if (i % 2 == 0) foot = 右足;
else
foot = 左足;
footを上げる;
footを前に出す;
footを下ろす;
}
}
これなら何歩でも問題ないよね
2.2 関数化
11
「歩く」関数を考える(4)
より広く利用可能な性質のことを汎用性と呼ぶ
►さっきの「何歩でも問題ない」がまさにそれ
►つまり完成した「歩く」関数は汎用性が高い
汎用性が高ければ高いほど再利用が可能
►コードを書く手間が省ける
►修正が一度で済む
何でもやってくれる関数は汎用性が低い
►その例を次のページで見てみよう
2.2 関数化
12
何でもやってくれる関数
例えばこんな関数を考える
►以下の手順を実行する関数
1. ユーザーからデータ入力を受け付ける
2. データを加工
3. データを保存
4. 完了メッセージを表示
最初から最後まで全部やってくれる良い関数
►果たしてそうかな?
2.2 関数化
13
何でもやってくれる関数の欠陥
用途が専用化している
►ファイルからデータを読み込みたい時は?
►データを保存せずそのまま使いたい時は?
►いちいち完了メッセージを出したくない時は?
要約すると、融通が利かない
►つまり汎用性が低いってこと
2.2 関数化
14
ちょっとずつやる関数にしよう
例えば以下の関数を作ったとしよう
►データ入力を受け付けるだけの関数
►データを加工するだけの関数
これは printf で
対処した方が良いかもね
►データを保存するだけの関数
►(メッセージを表示するだけの関数)
ファイルからデータを読み込みたい時は?
►ファイルからデータを読み込む関数を作れば良い
データを保存せずそのまま使いたい時は?
►保存するだけの関数を呼ばなければ良い
2.2 関数化
15
つまり汎用性の高い関数とは
関数にする意味がある程度のカタマリ
►例えば printf でメッセージを出すだけなら無意味
かつ
専用化しないようになるべく小さい機能
►「何でもやってくれる関数」にならないように注意
この2つを両立させると「良い関数」が出来る
►このバランスは経験によって身に付けよう
►たくさん書くことが大切
2.2 関数化
16
補足事項
見やすいコードを書こう(命名編)
►Wikiを見てね
http://nanzanmmc.net/pc/lecture/game/add
2.2 関数化
17