📄 ch02s01.html
字号:
<html><head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>1. Continuation初步</title><link rel="stylesheet" href="html.css" type="text/css"><meta name="generator" content="DocBook XSL Stylesheets V1.69.1"><link rel="start" href="index.html" title="Java网络程序员看Continuation"><link rel="up" href="ch02.html" title="Chapter 2. Continuation,call/cc函数与回退/刷新键"><link rel="prev" href="ch02.html" title="Chapter 2. Continuation,call/cc函数与回退/刷新键"><link rel="next" href="ch02s02.html" title="2. 较复杂的continuation应用:coroutine"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">1. Continuation初步</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch02.html">Prev</a> </td><th width="60%" align="center">Chapter 2. Continuation,call/cc函数与回退/刷新键</th><td width="20%" align="right"> <a accesskey="n" href="ch02s02.html">Next</a></td></tr></table><hr></div><div class="section" lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="d0e142"></a>1. Continuation初步</h2></div></div></div><p>Exception到底是什么?它其实就是一个goto:如果发生某某情况就跳到某某地方。但是它又不是一个普通的goto,因为它具体跳到哪里我们要到运行时才知道。当然,Exception只能跳到当前stack上的函数中。Exception的另一个看法是,它在try的地方创建程序当前的一个“快照”,该快照的下一个指令是catch处。当程序执行到throw时,电脑自动调用该快照,也即catch成为下一个指令。</p><p>事实上,在没有Exception的C里,程序员实现类似Exception结构的标准方法就是使用setjmp()和longjmp()两个函数。简单地说,setjmp()就是“储存快照”,而longjmp()就是 “跳至快照”。如果您查一查longjmp()的说明,您会发现下面的警告:</p><p>“<span class="quote">The longjmp() routines may not be called after the routine which called the setjmp() routines returns. (不能在调用setjmp()的函数返回后再调用longjmp()函数。)</span>”</p><p>换句话说,用setjmp()/longjmp()(别的Exception机制也是如此)只能跳至仍留在stack上的快照。Call/cc函数与Exception类似,但是它没有这个限制。即使一个函数已经返回,我们还是可以跳到函数运行时存储下的快照,这样该函数运行完时会再次返回。</p><p>下面我们来看一个简单的Ruby例子。我们看看call/cc的具体用法和如何用call/cc模拟Exception。记住,C/C++/Java一类的语言中没有相应的函数。</p><pre class="programlisting">def grand_child(cont) cont.call("GRANT CHILD")enddef child(cont) grand_child(cont) return "CHILD"enddef parent return callcc { |cc| child(cc) }endanswer = parent()puts answer</pre><p>运行该程序,我们得到输出GRAND CHILD。</p><p>我们来仔细看看这个例子。我们看到,callcc函数(Ruby里的call/cc)接受一个closure作为参数。Closure是很多functional语言和动态语言的核心概念,可惜的是,C/C++/Java等语言中也没有与之直接对应的概念。大致地说,closure就是一个匿名函数。上面callcc后面的closure大致相当于:</p><pre class="programlisting">def anonymous_function(cc) child(cc)end</pre><p>Callcc函数会马上调用这个closure。那么callcc传给这个closure的参数cc是什么呢?答案是current continuation(当前continuation)。您的下一个问题一定是,什么是current continuation?</p><p>简单地说,current continuation就是当前程序的一个快照。如果您记得我们之前讲过的,continuation passing style中剩下的操作会作为一个参数(continuation)传给子函数。顾名思义,current continuation的定义就是程序执行到现在还剩下的操作(这在逻辑上等价与程序当前的一个快照)。如果我们看看这个程序,那么很明显程序执行到callcc时剩下的操作是:return callcc的结果,将该值赋予answer,输出answer,然后退出。所以我们的closure接到的参数cc就是进行这些操作的一个“函数”,或者说continuation。</p><p>接下来我们看到,closure把这个continuation传给child,child又把它传给grand_child。这时,grand_child调用:</p><pre class="programlisting">cont.call(“GRAND CHILD”)</pre><p>这段代码的意思是,执行cont保存的continuation,并将字串GRAND CHILD作为callcc的返回值。换句话说,跳到continuation保存的快照处继续执行。如果我们看看该continuation所包含的操作,我们就可以知道接下来会发生什么:返回字串GRAND CHILD,赋值给answer,输出answer(我们会看到GRAND CHILD),然后退出。</p><p>现在我们重新看看这段代码,您能看出它事实上就实现了一个Exception机制吗?注意到continuation可以像别的值一样作为参数或者保存在全局变量中,所以尽管这里continuation看起来像Exception,但是我们可以用它跳至几乎任何地方,而不会有类似Exception的限制。</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch02.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ch02.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="ch02s02.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Chapter 2. Continuation,call/cc函数与回退/刷新键 </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> 2. 较复杂的continuation应用:coroutine</td></tr></table></div></body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -