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 .cfile
 
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
  - sumNA(- nA=3)
- nA > 0- 
      - sumNB(- nB=2)
- nB > 0- 
          - sumNC(- nC=1)
- nC > 0- 
              - sumND(- nD=0)
- nD ≦ 0
- return0
 
- return1 + 0
 
- return2 + 1
 
- return3 + 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
  - printNaryA(iA=83, nA=6)
- iA ≧ nA 
    
      - printNaryB(iB=13,
      nB=6)
- iB ≧ nB 
        
          - printNaryC(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
- ハノイの塔