K&Rを読もう(8) 1.7 図解Schemerの再帰脳

Schemerは再帰が無いと生きていけない。再帰で考えた方がシンプルに問題が解ける。

K&R 1.7のテーマはべき乗。丁度良い題材なので、Schemerの再帰脳を復習しておこう。

Schemerは再帰で考える。

2の10乗をやろう。

2の10乗。つまり、2を10回掛けるってこと。

展開してみる。

power(2, 10) = 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2

2のゼロ乗は1だから、1を掛ける。

power(2, 10) = 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 1

こんな感じ。

色々やってみよう。

power(2, 10) = 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 1
power(2, 9)  = 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 1
power(2, 8)  = 2 * 2 * 2 * 2 * 2 * 2 * 2 * 2 * 1
power(2, 7)  = 2 * 2 * 2 * 2 * 2 * 2 * 2 * 1

...(略

power(2, 2)  = 2 * 2 * 1
power(2, 1)  = 2 * 1
power(2, 0)  = 1

再帰的ですねぇ。

よく見てみるとパターンのある再帰的構造であることがわかる。

power(2, 10) = 2 * power(2, 9)
power(2, 9)  = 2 * power(2, 8)
power(2, 8)  = 2 * power(2, 7)
power(2, 7)  = 2 * power(2, 6)

...(略

power(2, 2)  = 2 * power(2, 1)
power(2, 1)  = 2 * power(2, 0)
power(2, 0)  = 1

power(2, 10) = 2 * power(2, 9)のように、一個前の結果を見れば良いことがわかる。数学っぽくすると、

power(base, 0) = 1
power(base, n) = base * pow(base, n - 1)

baseは基数、nは何乗するか。

ゼロの時は1で、nの時は(n - 1)の結果と掛ける。

後はプログラムに直すだけ。

// 再帰
int power_r(int base, int n)
{
    if (n == 0)
        return 1;
    return base * power_r(base, n - 1);
}

数学の定義と一緒だし、考え方もシンプルだ。

Schemerはこう考える。

  • 「一個前はなんだっけ」
  • 「終わりはなんだっけ」

それだけ考えれば良い。

for文に直すまでの道のり

僕の脳の中身をプログラムに置き換えてみる。

#include <stdio.h>
#include <stdlib.h>

// 再帰
int power_r(int base, int n)
{
    if (n == 0)
        return 1;
    return base * power_r(base, n - 1);
}

// 反復
int power_i(int base, int n)
{
    int iter(int p, int i)
    {
        if (i == 0)
            return p;
        return iter(p * base, i - 1);
    }
    return iter(1, n);
}

// 繰り返し(while)
int power_w(int base, int n)
{
    int p = 1;
    while ( n > 0) {
        p = p * base;
        --n;
    }
    return p;
}

// 繰り返し(for)
int power_f(int base, int n)
{
    int p = 1;
    for (; n > 0; --n)
        p = p * base;
    return p;
}

int main(void)
{
    int i;
    printf("%d\n", power_r(2, 10));
    printf("%d\n", power_i(2, 10));
    printf("%d\n", power_w(2, 10));
    printf("%d\n", power_f(2, 10));

    exit(EXIT_SUCCESS);
}

for文はフクザツ。

Schemerが再帰で考える訳がわかったかなぁ?