Creation and Use of Functions
(関数の作り方と使い方)
Computing Practice I
5th lecture, May 18, 2017
http://www.sw.it.aoyama.ac.jp/2017/CP1/lecture5.html
Martin J. Dürst
© 2005-17 Martin
J. Dürst 青山学院大学
Today's Schedule
- Minitest
- About previous exercises
- Functions
- Declarations and definitions
- Function extraction and refactoring
- Functions calling themselves, recursion
- About today's exercises
ミニテスト
- 授業開始までにログイン済み
- ナビゲーションは左に畳み、ブラウザは全画面に拡大
- 授業開始まで教科書、資料、筆箱、財布などを鞄に入れ、鞄を椅子の下に
- テスト終了後その場で待つ
Applications of C: Linux
- Open source operating system (kernel)
- Development started by Linus Torwalds to study operating systems;
first published in 1991
- Currently used in most servers, some PCs (Debian, Fedora, Ubuntu,...),
Android, embedded devices
- Usage is similar to Unix (origin of C language), but implementation is
different
Results of Previous Exercises
|
04A1 |
04A2 |
04A3 |
04B1 |
04B2 |
04C1 |
100 points |
85 |
81 |
87 |
57 |
31 |
1 |
60 points |
14 |
18 |
21 |
41 |
60 |
26 |
partial (10 points or more) |
- |
- |
- |
- |
- |
46 |
partial (less than 10) |
- |
- |
- |
- |
- |
14 |
error |
- |
- |
- |
- |
3 |
7 |
not submitted |
- |
- |
- |
1 |
5 |
5 |
Example Solutions for 04C1
- Solution strategies:
- Nested
switch
/if
s
- State machine
- Small buffer
- Hints:
- Don't read lines, read characters
- Treat newline like a regular character
- Treat EOF (almost) like a regular character
- Separate !=EOF test and getchar() call
- Establish invariants (e.g. always keep the next character ready in a
variable)
- Use notation that is simple, compact, and easy to understand
if (c == 97)
⇒ if (c == 'a')
putchar('&'); putchar('a');
⇒
printf("&a");
Importance of Functions
- Structure program by splitting it up into smaller pieces
- Avoid repeated code doing the same (or similar) things
- Separate What? from How?
- Abstract from How? to What?
Functions in Programming Languages and in Mathematics
- 数学的理論では「関数」さえあれば「計算」は万能
- 数学の関数と C の関数は少し違う:
- 数学: 結果は普遍、副作用 (side effects) なし
- C: 副作用あり (入出力、参照渡しなど)
- 関数型言語 (functional programming language):
副作用なしの関数のプログラム言語 (例: Haskell)
- 関数が多くて短いプログラムの方がいい
(オブジェクト思考言語や関数型言語では 10
行の関数は長い)
Concepts and Terms around Functions
- Function name
- Argument/parameter
- Formal parameter (defined in function, similar to variable)
- Actual parameter (value passed to function when called)
- Return value
How to Think about Functions
Clearly separate thinking about:
- Usage of a function
- Think about function name, number and types of parameters, type of
return value
- Ignore internal details of function implementation
- Construction/contents of a function
- Think about internal details of function implementation
- Ignore usage context (→ make usable in many different contexts)
- Never use global variables
- Avoid functions with side effects where possible
- Limit input/output to
main
function, or functions
dedicated to input only or output only
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 unnecessary 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 be direct or 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
Important Points about Recursive Functions
- Recursion helps simplify many problems
- Formal parameters and local variables exist separately for each
invocation
- Better to think about "a function doing the same work" rather than "the
same function"
(not "the same person", but rather "a person doing the same work")
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
- 関数
- operating system
- オペレーティングシステム
- embedded device
- 組込機器
- state machine
- 状態機械
- function name
- 関数名
- argument/parameter
- 引数 (ひきすう)
- formal parameter
- 仮引数
- actual parameter
- 実引数
- return value
- 戻り値 (または返り値)
- global variable
- グローバル変数
- side effect
- 副作用
- (function) invocation
- (関数の) 呼出
- declaration
- 宣言
- definition
- 定義
- function extraction
- 関数の抽出
- refactoring
- リファクタリング
- recursion
- 再帰
- recursive function
- 再帰的関数
- mathematical induction
- 数学的帰納法
- stack overflow
- スタック・オーバーフロー
- tail recursion
- 末尾再帰
- tower of Hanoi
- ハノイの塔