非递归方法实现递归
要达到这个目的, 首先需要理解什么是程序. 程序就是一些机器指令序列. 只需要顺序执行的简单语句和条件跳转语句, 就能实现任何程序, 所以想象这些指令序列都有一个帧栈维护, 每一帧都有一个pc值. 所以可以进行如下抽象
- 函数执行: 取出top的pc值, 执行对应的简单指令
- 函数调用: 往帧栈中push新的帧
- 函数返回: pop帧栈的一帧
例1: 非递归汉诺塔实现
递归实现汉诺塔可以参考递归练习-汉诺塔
// hanoi-r.c
void hanoi(int n, char from, char to, char via)
{
if (n == 1) {
printf("%c -> %c\n", from, to);
}
else {
hanoi(n - 1, from, via, to);
hanoi(1, from, to, via);
hanoi(n - 1, via, to, from);
}
}
如上图原理所示, 非递归的汉诺塔实现如下:
// hanoi-nr.c
typedef struct {
int pc, n;
char from, to, via;
} Frame;
//函数执行, push, 新push的函数总是pc等于0开始, 后续按顺序执行
#define call(...) ({ *(++top) = (Frame) { .pc = 0, __VA_ARGS__ }; })
//函数返回就是pop, 所以top--
#define ret() ({ top--; })
//简单的跳转指令
#define goto(loc) ({ f->pc = (loc) - 1; })
void hanoi(int n, char from, char to, char via) {
Frame stk[64], *top = stk - 1; // 定义帧栈, 和栈顶
call(n, from, to, via);
// hanoi函数总是按顺序执行, 所以pc的值一直++, 知道帧栈为空
for (Frame *f; (f = top) >= stk; f->pc++) {
n = f->n; from = f->from; to = f->to; via = f->via;
switch (f->pc) {
case 0: if (n == 1) { printf("%c -> %c\n", from, to); goto(4); } break;
// goto(4)表示, 直接执行ret(), 即函数返回
case 1: call(n - 1, from, via, to); break;
case 2: call( 1, from, to, via); break;
case 3: call(n - 1, via, to, from); break;
case 4: ret(); break;
default: assert(0);
}
}
}
例2: f函数调用g函数, g函数调用f函数.
递归实现为:
// fg-r.c
int g(int);
int f(int n)
{
if (n <= 1)
return 1;
return f(n - 1) + g(n - 2);
}
int g(int n)
{
if (n <= 1)
return 1;
return f(n + 1) + g(n - 1);
}
可以自己先思考一下
小提示:
将pc值分为两种, 一种f函数的pc值, 一种g函数的pc值.
所以函数调用的时候, 要判断取出的frame是f函数还是g函数, 这里我采用的方法是添加一个成员变量func
.
typedef struct {
int fpc, gpc;
char func; // 判断是f函数还是g函数
int n;
} Frame;
// 宏定义与上例类似
#define call(...) ({ *(++top) = (Frame) { .fpc = 0, .gpc = 0, __VA_ARGS__ }; })
#define ret() ({ top--; })
#define f_goto(loc) ({ f->fpc = (loc) - 1; })
#define g_goto(loc) ({ f->gpc = (loc) - 1; })
函数主体部分:
void stackFrame(int n, char func, int* res) {
Frame stk[32], *top = stk - 1;
call(func, n);
for (Frame *f; (f = top) >= stk;) {
int num = f->n;
if (f->func == 'f') {
switch(f->fpc) {
case 0: if(num <= 1) { (*res)++; f_goto(3); } break;
case 1: call('f', num - 1); break;
case 2: call('g', num - 2); break;
case 3: ret(); break;
default: assert(0);
}
f->fpc++; // 如果执行的是f函数, fpc就加一, 反之, gpc加一
} else if(f->func == 'g') {
switch(f->gpc) {
case 0: if(num <= 1) { (*res)++; g_goto(3); } break;
case 1: call('f', num + 1); break;
case 2: call('g', num - 1); break;
case 3: ret(); break;
default: assert(0);
}
f->gpc++;
}
}
}
int f_nr(int n) {
int res = 0;
stackFrame(n, 'f', &res);
return res;
}
int g_nr(int n) {
int res = 0;
stackFrame(n, 'g', &res);
return res;
}
测试
#include<stdio.h>
#include"fg-r.c"
#include"fg-nr.c"
int main() {
printf("%d\n", f(4));
printf("%d\n", f_nr(4));
printf("%d\n", g(5));
printf("%d\n", g_nr(5));
}
输出结果如下