Creation and Use of Functions
(関数の作り方と使い方)
Computing Practice I
5th lecture, May 12, 2016
http://www.sw.it.aoyama.ac.jp/2016/CP1/lecture5.html
Martin J. Dürst
Today's Schedule
- Minitest
- About last week's exercises
- Functions
- Declarations and definitions
- Function extraction and refactoring
- Functions calling themselvel, recursion
- About today's exercises
ミニテスト
- 授業開始までにログイン済み
- 授業開始まで教科書、資料、筆箱、財布などを鞄に入れ、鞄を椅子の下に
- テスト終了後その場で待つ
C 言語の応用: Linux
- オープンソース OS (のカーネル)
- Linus Torwalds が OS の勉強のために開発を開始、
1991年に最初の公開
- 現在多くのサーバ、一部の PC (Debian, Fedora,
Ubuntu,...)、Android、組込機器で利用
- C の原点の Unix と使い方が同じが、中身は別
前回の演習結果
|
04A1 |
04A2 |
04A3 |
04B1 |
04B2 |
04C1 |
100点 |
70 |
64 |
66 |
53 |
11 |
1 |
60点 |
18 |
24 |
22 |
34 |
70 |
23 |
部分点 |
- |
- |
- |
- |
- |
36 |
エラー |
- |
- |
- |
1 |
3 |
11 |
未提出 |
1 |
1 |
1 |
1 |
5 |
18 |
前回の演習問題について
- 新しい内容はこれからも増える一方 →
予習、復習を一層強化
- 金曜日の午後、研究室 (O-527/529)で質問可 (~19:30まで)
- 宿題でも Q&A フォーラムをもっと利用
(提出締切が日曜日の場合、土曜 24:00
まで)、題名に注意
(2015年: 21件; 2016年: 4件)
Example Solutions for 04C1
- Don't read lines, read characters
- Treat newline like any other character
- Separate !=EOF test and getchar() call
- Invariant: Always keep the next character is the same variable
- Don't forget initialization
if (c == 97)
⇒ if (c == 'a')
putchar('&'); putchar('a'); putchar('m');
⇒
printf("&am");
Importance of Functions
- プログラムを小分け、構造化
- 同等な処理や類似する処理を一回だけ記述
- 「何を」と「どうやって」の分離
- 「どうやって」から「何」への抽象化
プログラム言語の関数と数学の関数
- 数学的理論では「関数」さえあれば「計算」は万能
- 数学の関数と C の関数は少し違う:
- 数学: 結果は普遍、副作用 (side effects) なし
- C: 副作用あり (入出力、参照渡しなど)
- 関数型言語 (functional programming language):
副作用なしの関数のプログラム言語 (例: Haskell)
- 関数が多くて短いプログラムの方がいい
(オブジェクト思考言語や関数型言語では 10
行の関数は長い)
関数関係の概念
- 関数名 (function name)
- 引数 (ひきすう、argument, parameter)
- 仮引数 (formal parameter、関数で定義、変数と同様)
- 実引数 (actual parameter、呼び出し時に渡す値)
- 戻り値 (return value、返り値とも)
関数の考え方の基本
関数の作る方と使う方を全く別のこととして考える!
- 使う方では関数の名前、引数の数と型、戻り値の型だけ考える
- 作る方では関数の中身だけ考える
- グローバル変数は禁止
- 副作用の関数は極限避ける
- 入出力は
main
関数で
(大きいプログラムでは main
関数に近く、入力や出力専用の関数で、計算の関数と別)
Declarations and Definitions
Distinguished for functions (and global variables)
- Declaration
- Declares existence of identifier, name, and type
- Example:
long fibonacci(int i);
- If functions are used across files, declarations are included in a
.h
(header) file
- Definition
- Defines actual presence, memory use, initialization, working
details
- A definition also serves as a declaration
- Example:
long fibonacci(int i)
{ ... }
- Always in
.c
file
Attention: Our coursebook uses a lot of unnnecessary declarations:
int main(void); // this declaration is
unnecessary
int main(void)
{...
関数の抽出の手順
- (空の) 新関数を作成 (意図に合った名前)
- 抽出したいコードを新関数にコピー
- 抽出したコード内のローカル変数の使用をチェック
→新関数のローカル変数または引数
- 抽出したコードでのローカル変数の変更をチェック
→一個の場合、新関数の戻り値
→複数の場合、関数の抽出を再検討
- コンパイル (新関数の文法チェック)
- 元の場所で抽出されたコードを新関数の呼出しに変更
- 新関数だけで使用された変数を元の場所で削除
- コンパイル、テスト
(Refactoring: Improving the Design of Existing Code, Martin Fowler et
al., Addison-Wesley, 1999 の「メソッドの抽出より」)
Refactoring
- 更なる機能の追加の準備や
デサインの向上のため、
プログラムの機能を変更せずに
プログラムを書き直す
- 簡単にできる小さなプログラムの変更
- 例:
- 一時変数の導入 (または削除)
- 関数の名前の変更
- 関数の抽出 (又はインライン化)
- オブジェクト指向プログラミングでよく使われるが、C
でも利用可能
- テストとの組み合わせで力を発揮
A Function Calling Itself
Very simple example: Calculating the sum of integers from 1 to n
(∑ni=0 i):
int sumN (int n)
{
if (n <= 0)
return 0;
else
return n + sumN(n-1);
}
Mathematical justification:
∑ni=0 i = n
+ ∑n-1i=0 i (for
n>0); ∑0i=0
i = 0
Execution Example for sumN
sumN
A(n
A=3)
n
A > 0
sumN
B(n
B=2)
n
B > 0
sumN
C(n
C=1)
n
C > 0
sumN
D(n
D=0)
n
D ≦ 0
return
0
return
1 + 0
return
2 + 1
return
3 + 3
Result: 6
Recursive Functions
- A function that calls itself is called a recursive function
- The principle is called recursion
- Recursion can also be indirect (e.g. a()→b()→a())
- Recursion is strongly related to mathematical induction
- For some problem specifications, recursive functions may be very easy to
write
One More Example of a Recursive Function
Convert a number i to n-ary representation:
void printNary (int i, int n) {
static char letters[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if (i < n)
putchar (letters[i]);
else {
printNary (i/n, n);
putchar (letters[i%n]);
}
}
Execution Example for printNary
printNary
A(iA=83, nA=6)
- iA ≧ nA
printNary
B(iB=13,
nB=6)
- iB ≧ nB
printNary
C(iC=2,
nC=6)
- iC < nC
putchar
('2')
putchar
('1')
putchar
('5')
Result: 215
再帰的関数の要点
- 問題の分担、簡単化が可能
- 引数とローカル変数は呼び出しごと別に存在
- 「同じ関数」のではなく「同じような関数」で考えた方がよい
(「同じ人」) ではなく、「同じ仕事をこなせる人」)
Parts of a Recursive Function
- Non-recursive base case
- Important: Has to come first
- Otherwise, there may be infinite recursion
(resulting in a stack overflow or segmentation fault)
- Actual recursion
Recursion Patterns
- Recursion, processing (
sumN
, printNary
,...)
- Processing, recursion (tail recursion)
- Processing, recursion, processing (05C2)
- Recursion, recursion, processing (Fibonacci function)
- Recursion, processing, recursion (tower of Hanoi)
[recursion: recursive function call; processing: data processing local to
the function]
プログラム開発のコツ
(advice for program development)
- 大きいプログラムを小さい部分に分ける
- 開発は小さい部分で:
- 全体をおおざっぱに分けるだけ (top-down)
- 一部の小さい機能から (bottom-up)
- 常に正しいインデント、読みやすさを維持
- 常にコンパイルできる状態を維持
- まめにコンパイル・実行・テスト
問題再利用の目的
- 実世界でプログラムの更新が多い
- リファクタリングとテストの練習
- 時間の短縮 (短い時間で長い問題に挑戦可能)
- 自分のプログラムの読み直し (何週間後で再読)
- 複数のプログラミングスタイルの比較
演習問題の概要
- 05A1: 簡単なプログラムから階乗関数を抽出
- 05B1: Josephus 関数
- 05B2: Fibonacci 関数 (数列) の再帰的プログラム
- 05B3: 組み合わせの表の計算
- 05C1: 学生番号分析プログラムを関数で書き直す
(リファクタリングとテストの応用)
- 05C2
(発展問題、月曜締切、部分点無し、ネタバレに厳重注意):
再帰を使って入力を逆順にする
仮名の関数、名前が無意味の関数など減点対象
ヒント: 入力の関数の戻り値をうまく利用
(05C1 以外は全部非常に短い)
次回の準備
- 今日の復習
- 残った演習問題を宿題として完成させる
- 教科書の第 10 章 (構造体, pp. 242-279) を読む (pp. 272-273
(
typedef
) を含む)
Glossary
- function
- 関数
- declaration
- 宣言
- definition
- 定義
- function extraction
- 関数の抽出
- refactoring
- リファクタリング
- recursion
- 再帰
- recursive function
- 再帰的関数
- mathematical induction
- 数学的帰納法
- stack overflow
- スタック・オーバーフロー
- tail recursion
- 末尾再帰
- tower of Hanoi
- ハノイの塔