サイトマップ / C言語講座出入り口総目次目次:再帰>関数の再帰呼び出し

青い直線

関数の再帰呼び出し

青い直線

[2次元ポインタ配列]←このソース→[10進数を16進表示]

/* 再帰呼び出し

今日は階乗を求める関数を例にして、再帰呼び出しについて学びます。再帰呼び出し(Recursive call)とは、関数の中で自分自身と少し異なる自分を呼び出すことです。全く同じ自分を呼び出すことではありません。無限ループになってしまいます。

再帰呼び出しの関数のコードを記述するのは、慣れないと難しいのですが、再帰に向いているルーチンならば、短くて強力なソースプログラムを書くことができます。

まず、ある整数の階乗を返す関数を再帰を使わないで作ります。

0の階乗:1
1の階乗:1
2の階乗:2 * 1
3の階乗:3 * 2 * 1
- - - - - - -
nの階乗: n * (n - 1) * (n - 2) - - - 3 * 2 * 1
- - - - - - -

nの値が大きくなるにつれて、nの階乗の値は急速に増加します。そこで戻り値は long型の整数とします。階乗を英語では" Factorial "というので、nの階乗を求める関数の名前を下記のようにします。

    long Factorial1(int n) ;

nが0か1の時

0の階乗:1
1の階乗:1

なので、上記の2行をそのままソースコードにすると、次のようになります。

    if (n == 0)
        return (1L);
    else if (n == 1)
        return (1L);

しかし、これは次に示すように、論理演算子(|| :論理和)を使えばもっと簡潔に記述できます。

    if (n == 0 || n == 1)
        return (1L);

では、nが3より大きい場合について考えます。ループを使えば良さそうです。long型の変数pを宣言し、1Lに初期化しておく。ループの中で、もし、nが1より大きければ、pにnを掛け、nの値を1減少させる。

上記の文をそのままコードにすると、関数は次のようになります。

    long Factorial1(int n)
    {
        long p = 1L;

        if (n == 0 || n == 1)
            return (1L);
        else if (n > 1) {
            for ( ; n > 0; n--)
                p = p * n;	// * 注
            return (p);
        }
    }	
	* 注:pはlong型で、nはint型なので、
	この掛け算はnがlong型に変換されてから行われる。
	キャストの必要はない。

次に再帰版を作ります。

    long Factorial2(int n);

Cでは、関数を呼び出す時、スタックに値を積みます。呼ばれた関数の側は積まれた値を利用します。これを値による呼び出し(Call by value)といいます。再帰版のソースを下記に示します。

      // 階乗を求める(再帰版)
    long Factorial2(int n)
    {
        if (n == 0 || n == 1)    // n が 0 か 1 なら
            return (1L);         // 1 を返す
        else   // n に自分より 1 小さい自分を掛ける(これが再帰呼び出し)
            return (n * Factorial2(n -1));
    }

この関数を呼び出すと何が起こるか、次の説明をじっくり眺めて下さい

Factorial2(n)の中に、n * Factorial2(n -1);と記述してあります。メインルーチンからFactorial2(n)が呼ばれると、次の一連の出来事が起こります。

スタックに n が積まれ、Factorial2(n-1) が呼ばれる
スタックに n-1 が積まれ、Factorial2(n-2) が呼ばれる
スタックに n-2 が積まれ、Factorial2(n-3) が呼ばれる
- - - - - - - - - - - - - - - - - - - - - - - -
スタックに 3 が積まれ、Factorial2(2) が呼ばれる
スタックに 2 が積まれ、Factorial2(1) が呼ばれる

Factorial2(1)が呼ばれると、順次スタックに積まれた値とのかけ算をしながら戻ります。 最後に、Factorial2(n)で、 n * Factorial2(n -1)の計算が行われ、 Factorial2(n)を呼び出したルーチンに戻ります。 その結果、階乗が求められます。

ソースプログラムの再帰版と非再帰版の長さを比べて下さい。再帰版では短くなったと思います。 */

/* ここからソースプログラム */

#include <stdio.h>

long Factorial1(int n) ;
long Factorial2(int n) ;
void main(void);

  /* 階乗を求める(非再帰版) */
long Factorial1(int n)
 {
    long p = 1L;

    if (n == 0 || n == 1)       /* nが0か1なら */
        return (1L);            /* 1を返す */
    else if (n > 1) {
        for ( ; n > 0; n--)
            p = p * n;
            return (p);
    }
}	

  /* 階乗を求める(再帰版) */
long Factorial2(int n)
 {
    if (n == 0 || n == 1)        /* nが0か1なら */
        return (1L);             /* 1を返す */
    else                         /* nに自分より1小さい自分を掛ける */
        return (n * Factorial2(n -1));
}

void main(void)
{
    int n;
    long f;

    printf("整数を入力して下さい\t");
    scanf("%d", &n);

    f = Factorial1(n);
    printf("Factorial1(  ) = %ld\n", f);

    f = Factorial2(n);
    printf("Factorial2(  ) = %ld\n", f);
}

/* ここまでソースプログラム */

[2次元ポインタ配列]←このソース→[10進数を16進表示]

青い直線

/* (C) 2000- YFプロ. All Rights Reserved. */    提供:C言語講座−それ自体コンパイルできる教材を使った講座です−

青い直線

サイトマップ / C言語講座出入り口総目次目次:再帰>関数の再帰呼び出し