📄 ch04s03.html
字号:
CpsFib.prnRetF(n, sf, nextOp); }}class RetFCont extends Continuation { RetFCont(StackFrame sf) { super(sf); } void go() { CpsFib.retF(sf, nextOp); }}public class CpsFib { static void cpsAdd(int a, int b, StackFrame sf, Continuation cont) { cont.go(a + b); } static void cpsPrint(int n, int f, StackFrame sf, Continuation cont) { System.out.println("Fib(" + n + ") = " + f); cont.go(); } static void fib(int n, StackFrame sf, Continuation cont) { FibStackFrame fsf = (FibStackFrame) sf.prepend(new FibStackFrame()); fsf.n = n; if (fsf.n <= 1) { cpsPrint(fsf.n, fsf.n, fsf, cont.prepend(new RetNCont(fsf))); } else { fib(n - 1, fsf, cont.prepend(new FibAddPrnRetFCont(fsf))); } } static void retN(StackFrame sf, Continuation cont) { cont.go(((FibStackFrame) sf).n); } static void fibAddPrnRetF(int n, StackFrame sf, Continuation cont) { FibStackFrame fsf = (FibStackFrame) sf; fsf.f1 = n; fib(fsf.n - 2, fsf, cont.prepend(new AddPrnRetFCont(fsf))); } static void addPrnRetF(int n, StackFrame sf, Continuation cont) { FibStackFrame fsf = (FibStackFrame) sf; fsf.f2 = n; cpsAdd(fsf.f1, fsf.f2, sf, cont.prepend(new PrnRetFCont(fsf))); } static void prnRetF(int n, StackFrame sf, Continuation cont) { FibStackFrame fsf = (FibStackFrame) sf; fsf.f = n; cpsPrint(fsf.n, fsf.f, sf, cont.prepend(new RetFCont(fsf))); } static void retF(StackFrame sf, Continuation cont) { FibStackFrame fsf = (FibStackFrame) sf; if (fsf.n == 4) { CpsFib.cont = cont; System.out.println("Continuation recorded."); } cont.go(fsf.f); } static Continuation cont; public static void main(String[] args) { StackFrame sf = new StackFrame(); fib(5, sf, new TerminateCont(sf)); System.out.println("Rerun continuation.."); cont.go(3); System.out.println("Rerun continuation.."); cont.go(1000); }}</pre><p>这段代码比基于Stack的要麻烦多了,因为CPS程序本身就非常难懂。我们来简要看看它的运作和结果。</p><p>在retF()中,如果n=4的话我们就会把当前continuation纪录在全局变量CpsFib.cont中。函数fib()返回后,main()会先调用cont.go(3),然后调用cont.go(1000)。第二个操作的意思是:从纪录下来的continuation处继续运行,但是假装refF()在此处返回1000(正常情况下fib(4)应该返回3)。上面程序的输出如下:</p><pre class="programlisting">Fib(1) = 1Fib(0) = 0Fib(2) = 1Fib(1) = 1Fib(3) = 2Fib(1) = 1Fib(0) = 0Fib(2) = 1Fib(4) = 3Continuation recorded.Fib(1) = 1Fib(0) = 0Fib(2) = 1Fib(1) = 1Fib(3) = 2Fib(5) = 5Rerun continuation..Fib(1) = 1Fib(0) = 0Fib(2) = 1Fib(1) = 1Fib(3) = 2Fib(5) = 5Rerun continuation..Fib(1) = 1Fib(0) = 0Fib(2) = 1Fib(1) = 1Fib(3) = 2Fib(5) = 1002</pre><p>注意比较Continuation recorded之后的一段输出和Rerun continuation..之后的两段输出。它们几乎是一样的,但是第二次我们让fib(4)返回是我们让它返回了1000,所以最后得到Fib(5)=1002。</p><p>这段代码是如何完成不可能的任务的?我们进行的主要修改如下:</p><div class="orderedlist"><ol type="1"><li><p>首先,每个函数当然多接受一个continuation为参数。不过,我们又增加了一个参数,StackFrame。原程序中的所有本地变量都放到这个StackFrame中去了。记得本文最早的CPS例子吗?当时我们把本地变量都变成了全局变量。那种做法比较简单,但是有递归函数时我们就不得不自己做一个stack了。</p></li><li><p>注意我们是如何处理这个stack的。每个StackFrame有一个指针指向下一个StackFrame,所以一个StackFrame其实就表示了一个Stack,并且我们的整个Stack是保存在Continuation的。注意到函数fib()会创建一个新的StackFrame,其他的函数则会把这个StackFrame一步一步传下去,直到retN()和retF()两个函数。如果fib()调用fib()的话新创建的StackFrame会指向原来的StackFrame,所以我们实际上在heap里创造了一个Stack。相应的,在retN()和retF()两个函数中,当前StackFrame的指针就没有再传下去了,所以如果没有另外的Stack包含了当前StackFrame的话,当前StackFrame就可以回收了。这里我们对Stack的操作和compiler自动生成的Stack在逻辑上是一模一样的,只是我们的stack在heap中。</p></li><li><p>我们为每一个函数创建了一个continuation类。逻辑上我们只是需要在continuation中保存一个函数指针,不过在Java里最简单的解法之一便是创造这些continuation的子类。</p></li></ol></div><p>程序的大部分代码相当的简单重复,这里就不再解释了。我们对Stack的处理其实并不很复杂,主要问题是CPS程序本身太难看懂。</p><p>上面基于heap的方法与基于stack的方法相比,最大的好处是基于heap法不用复制stack。不过,把程序变成CPS格式相当麻烦;相比之下,复制stack的代码就非常的简单固定。另一方面,把Java整个变成CPS并不太现实,因为Java里的函数调用太多,变成CPS的话会产生极多的函数。所以一般而言,基于stack的解决方案在Java上比较可行。</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch04s02.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ch04.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="ch05.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">2. 在Java上实现Continuation:基于stack法 </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Chapter 5. RIFE简介</td></tr></table></div></body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -