当然能。
所谓递归无非就是函数(直接或间接)调用自己,所以我们先看一下简单的函数调用。
假设有一个函数长这样:
int foo() {
int X = 4
int Y = bar(14);
return X*Y;
}
你可以把它变成这样:
int foo() {
foo_stack_frame = gcalloc { X: int; Y: int }
foo_stack_frame->X = 4;
foo_stack_frame->Y = bar(14);
return foo_stack_frame->X * foo_stack_frame->Y;
}
其中gcalloc就是指从GC heap上分配一个什么东西,这里分配的是一个map,用于保存foo的stack frame。
然后,foo可以变成这样:
int foo(continuation C) {
foo_stack_frame = gcalloc { C: continuation; X: int; Y: int }
foo_stack_frame->C = C;
foo_stack_frame->X = 4;
continuation Next = { foo2, foo_stack_frame };
return bar(Next, 14);
}
int foo2(continuation C, int Y) {
foo_stack_frame = C->stack_frame;
foo_stack_frame->Y = Y;
continuation Next = foo_stack_frame->C;
return Next->F(Next, foo_stack_frame->X * foo_stack_frame->Y);
}
这里的continuation你可以认为就是一个“返回地址”,这个等下再说。
你很可能会问,这么一大堆变换到底是在干神马?
嗯,其实这一堆就只变换干了一件事,那就是——把所有的函数调用变成了tail call。
大家都知道,tail call是可以直接优化成goto的,所以我们必须记录下原本的返回地址,把它传递给被tail call的函数,这样它就可以在结束的时候直接返回到最初的调用者那里。
如果对每个函数都做这样的变换,让每个函数调用都是tail call,那我们就彻底干掉了stack,整个程序只用goto就搞定。
PS. 对上面内容有兴趣的童鞋可以去看看 http://en.wikipedia.org/wiki/Continuation-passing_style
PPS. 其实从“理解”这个角度而言,递归往往更容易更清晰,因为它天生就是一个把高复杂度问题分解为低复杂度的过程。