📄 ch01s04.html
字号:
<html><head> <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> <title>4. CPS与网络流程控制</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="ch01.html" title="Chapter 1. CPS与网络程序流程控制"><link rel="prev" href="ch01s03.html" title="3. CPS与goto"><link rel="next" href="ch02.html" title="Chapter 2. Continuation,call/cc函数与回退/刷新键"></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">4. CPS与网络流程控制</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch01s03.html">Prev</a> </td><th width="60%" align="center">Chapter 1. CPS与网络程序流程控制</th><td width="20%" align="right"> <a accesskey="n" href="ch02.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="d0e107"></a>4. CPS与网络流程控制</h2></div></div></div><p>看了上面的例子,您可能很迷茫。如果CPS程序这样古怪,我们为什么要用它写网络程序?难道我们突然发现goto比while好用?这个问题的答案正好是反过来的:传统的网络程序其实就是用CPS风格写成的(即goto),而我们即将用continuation的概念重新“发明”while结构。</p><p>您不相信?我们再来看看猜数字这个游戏。如果现在您要写一个Java或者PHP的网络猜数字游戏,您会怎么写?让我来猜猜,您会写一个welcome页面,该页面也负责初始化整个游戏,例如将答案设置成一个随机数。然后您会写一个prompt页面,上面说:Guess a number [0, 100],后面是一个文本框。当用户按submit按钮之后,他的答案会提交到guess页面,该页面会告诉他答案是太大还是太小还是正确。如果答案正确,那么用户会被redirect至finishgame页面,否则会redirect回prompt页面。如此等等。您发现什么问题了吗?</p><p>不错,您正在用CPS风格,就算您之前从来没有听说过continuation。网络的welcome页面对应于CPS的main函数,prompt页面对应于prompt函数,guess页面对应于guess函数,finishgame页面对应与finishgame函数。逻辑中的while结构在两者中都没有了,改由prompt和guess之间的互相调用(也即goto)来完成。</p><p>网络程序之所以如此古怪,就是因为我们有一个CPS结构的print。每次生成页面之后,我们的servlet或者PHP script就中止了。不过,在我们的页面中却隐藏了很多continuation,又名URL。用户操作之后,我们的程序从这些continuation继续执行。所以,我们在不知不觉中都在用CPS,其问题也随之而来:</p><div class="orderedlist"><ol type="1"><li><p>凡是不止一个页面要用到的变量都必须放在session中,事实上,就是变成了全局变量。</p></li><li><p>没有while等控制结构。这正是cocoon control flow等技术试图解决的问题,可惜的是,这些解决方法往往要引入别的xml格式或者框架,与我们的第一个猜数字游戏的简洁无法比拟。</p></li><li><p>把控制传递给另一个页面容易,调用另一个页面却很难。 比方说,您可以写一个页面,让用户用各种方法查询一个航班号。可是如果某个程序有几个表格,每个表格里有几个航班号需要填写,您能不能在每个文本框旁边加一个“查询”链接?也许您可以,但是您不得不自己想一个办法来通知该查询页面,找到航班号之后怎么办?返回哪个页面?对该页面的数据进行什么修改?而在桌面程序中,查询页面只须返回该航班号,调用该页面的代码就可以继续执行对该航班号的操作,比如加入到某个文本框中。另外,多谢老天似乎没有人在网络上写需要回溯的程序,否则我们需要自己对堆进行管理。本文后面还会讲到这个问题。</p></li><li><p>在网络程序中,我们其实还不能直接用continuation。我们首先要把该continuation变成一个字符串藏在URL中,例如item=4&action=delete,等到客户端提交请求时再处理URL中的字串,才能获得该continuation,例如,performDeleteAction(getItemByIndex(4))。换个常用的说法,就是页面间只能传递string而不能传递对象或者程序状态。</p></li></ol></div><p>如果我们只能这样批评一下网络程序,那么您也就不需要看这篇文章了。其实,最重要的一点在于, 如果语言支持真正的continuation,那么我们可以很容易地实现CPS和非CPS程序间的互相调用。所以,在这些语言中,您可以用上面第一种方法写猜数字游戏,让compiler,runtime machine和一个框架来处理讨厌的CPS部分。</p><p>所以,接下来就让我们来看一个真正支持continuation的语言,Ruby,以及用以提供该支持的核心函数,call/cc。这里先提醒您一下,正确使用call/cc非常困难。如果您觉得下一章难以理解,也不要觉得受到挫折,因为实际上我们总会用一些框架来处理这些底层的问题,所以网络程序员几乎永远不会直接用到call/cc本身。本文后半部分就会介绍这样的两个框架。</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch01s03.html">Prev</a> </td><td width="20%" align="center"><a accesskey="u" href="ch01.html">Up</a></td><td width="40%" align="right"> <a accesskey="n" href="ch02.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">3. CPS与goto </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Chapter 2. Continuation,call/cc函数与回退/刷新键</td></tr></table></div></body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -