cp-9. 再帰関数
C プログラミング入門)
URL: https://www.kkaneko.jp/pro/adp/index.html
1
金子邦彦
内容
例題1.スタック
局所変数,大域変数
例題2.再帰関数による総和
2
目標
局所変数を使った関数を理解する
関数の再帰呼び出しを使って,簡単な漸化式の
計算を行
3
スタックのプッシュ(push), ポップ(pop) を行
関数を作る
次の2つの大域変数を使こと
1. 配列stack
2. スタックポインタtop
4
例題1.スタック
スタックのプッシュとポッ
プッシュ
(入れる操作)
ポップ
(出す操作)
すでに
入っていた
データ
積み重なる
に入る
すでに
入っていた
データ
一番最後に入った
データが先に出る
5
スタックとは
入れた順に積みあがっていく
1 1
2
3
2
1
2
1
2
1
4
push push 2 push 3 pop push 4
6
#include <stdio.h>
int stack[100];
int top=0;
void push(int n)
{
stack[top]=n;
top++;
return;
}
int pop()
{
int n;
top--;
n = stack[top];
return n;
}
push関数
pop関数
7
スタック
main 関数の例
int main()
{
push(1);
push(2);
push(3);
printf( "%d\n", pop() );
push(4);
printf( "%d\n", pop() );
printf( "%d\n", pop() );
printf( "%d\n", pop() );
return 0;
}8
スタック
実行結果の例
3
4
2
1
9
関数呼び出しの流れ
main 関数
int main()
push(1);
push(2);
push(3);
printf( "%d\n", pop() );
push(4);
printf( "%d\n", pop() );
printf( "%d\n", pop() );
printf( "%d\n", pop() );
関数呼び出し 戻り
push 関数
void push( int n )
return;
戻り
pop 関数
int pop()
return n;
10
配列によるスタックの実現
スタック
十分に多くのデータを格納できる配列stackを用意.
スタックポインタ
現在の「空き部分の底」を示す値(スタックポイ
ンタ)を格納させる変数topを用意.
スタックの底は stack[0],スタックの頂上は
stack[top-1]
配列
スタックの底 stack[0]
スタックの頂上 stack[top-1]
空き部分の底 stack[top]
11
配列によるスタックの実現
push 時: pushの後にtopの値を1足す
pop 時: popの前に,topの値を1引く
1 1
2
3
2
1
2
1
2
1
4
push push 2 push 3 pop push 4
1
top 2 3 4 3 4
12
局所変数
関数の仮引数と,関数本体内で宣言された変
数のこと
関数の内部でだけ有効
ほかの関数(main関数など)からは直接アクセス
できない
ある関数の局所変数と,呼び出した関数中の
局所変数と,例え同じ名前であっても,別の
実体である
13
大域変数
関数の外側で宣言された変数のこと
例) 配列stackとスタックポインタtopは,push
関数とpop関数のいずれからも参照できる.
#include <stdio.h>
int stack[100];
int top=0;
int push(int n)
{stack[top]=n;
top++;
return n;
}
int pop()
{int n;
top--;
n = stack[top];
return n;
}
これが大域変数
push関数と
pop関数で
共有される
14
局所変数と大域変数
局所変数 大域変
宣言の場所 関数の仮引数
または関数内
関数の外側
有効範囲 関数の内部だ
けで有効
複数の関数で
共有される
15
スタックの使用例
正整数を読み込んだら,pushする.
負整数を読み込んだら,popして,取り出された
を表示する.
が読み込まれるまで上の操作を繰り返す.
16
#include <stdio.h>
#pragma warning(disable:4996)
int main()
{
int item;
top= -1;
do {
printf("Enter a number ");
scanf("%d",&item);
if (item>0) {
printf("pushed: %d\n",item);
push(item);
} else if (item<0) {
printf("popped: %d\n",pop());
}
} while (item!=0);
return 0;
}17
課題1.スタック操作の例外処理
スタック操作のプログラムにおいて,
push 関数と pop 関数を書き換えて,
次に挙げた,2つの例外事象の対処を考慮しなさい
1.スタックが空のときにポップ(popしない.
スタックが空のときに pop 関数が呼び出されると,「Stack
empty」のエラーメッセージを表示すること
2.スタックが満杯のときにプッシュ(pushしない.
スタックが満杯のときに push 関数が呼びだされると,
Stack full」のエラーメッセージを表示すること
18
課題1のヒント
配列
スタックポインタの
値は 「0」
空のとき 満杯のとき
スタックポインタの
値は,「配列のサイズ」
スタックポインタは,現在の「空き部分の底」の
位置を示す値 「スタック内のデータ数」に等しい
19
例題2.再帰関数による総和
整数Nから,1からNまで総和を求める再帰関数
を作る
例) 5 → 15
再帰関数とは,関数 fの本体にfの呼出しを含むよ
な関数のこと
otherwise
1 if11
1
1
n
i
n
iin
n
i
20
再帰関数とは
自分自身を呼び出すな関数のこと
必ず終了条件が成立するよに気をつけるこ
21
#include <stdio.h>
#pragma warning(disable:4996)
int sum(int n)
{if (n < 1) {
return 0;
}
if (n == 1) {
return 1;
}
else {
return n+sum(n-1);
}
}
int main()
{int n;
int s;
printf("Enter a number: ");
scanf("%d",&n);
s = sum(n);
printf("sum(%d)=%d\n",n ,s);
return 0;
}
sum関数
main関数
22
再帰関数による総和
実行結果の例
Enter a number: 2
sum(2)=3
23
関数呼び出しの流れ
main 関数で n = 2 のとき)
main 関数
int main()
sum(n);
関数呼び出し sum 関数
int sum( int n )
return n + sum(n-1);
関数呼び出しと戻
sum 関数
int sum( int n )
return 1;
戻り
24
n の値の変化
main 関数で n = 2 のとき)
main 関数
int main()
sum(n);
関数呼び出し sum 関数
int sum( int n )
return n + sum(n-1);
関数呼び出しと戻
return
1;
戻り
n = 2
sum 関数
int sum( int n )
n = 1
1を返す
3を返す
25
再帰関数による総和
n == 1 No
Yes
return 1;
return n + sum(n-1);
26
プログラム実行順
main関数で n=2 のとき)
s = sum( n );
main 関数
int main()
n ==1
return n + sum(n-
1);
No
Yes
return 0;
sum 関数
int sum( int n )
関数
呼び出し
戻り
printf( "Enter a number:" );
scanf( "%d", &n );
return 0;
printf("sum(%d)=%d\n",n ,s);
n ==1
return n + sum(n-
1);
No
Yes
return 1;
sum 関数
int sum( int n )
関数
呼び出し
戻り
27
データの流れ
s = sum( n);
return n + sum(n-1);
関数
呼び出し
戻り
n の値を,
sum 関数に渡す
返された値を
s に格納する
渡された値は,
n」とい名前で使
n-1 の値を,
sum 関数に渡す
値「3」を返す
n2
プログラム
データ
プログラム
main 関数
int main()
bar関数
int sum( int n )
n2
データ
1
return1;
プログラム
bar関数
int sum( int n )
n
データ
1
渡された値は,
n」とい名前で使
関数
呼び出し
値「1」を返す
戻り
28
return n+sum(n-1);
int sum(int n)
{if (n < 1) {
return 0;
}
if (n==1) {
return 1;
}
else {
return n+sum(n-1);
}
}
呼び手
戻り値の受け取り
引数仮引数にセットする
29