Example

您所在的位置:网站首页 高大威武是什么意思你显示出来吧 Example

Example

2023-01-11 09:18| 来源: 网络整理| 查看: 265

f B 灵计 _齟 鼷雰 M 书 . .r' . . I PEARSON Computer Science An Overview [ teviMiiii Lulitioii 计算机 f 样概论 本书多年来一直深受世界各国高校师生的 欢迎, 是美国哈佛大学、麻省理工学院、普林斯顿大学、加州大学伯克利分 校等著名大学的首选教材 f 对我国的高校教学也产生了广泛影响 3 本书以历史眼光*从发展的角度、当前的水平以及现阶段的研究方向等几个方面,全景式描绘了计算机科学各个子学 科的主要领域。在内容编排上,很好地兼顾了学科广度和主题深度,把握了最新的技术趋势。本书用算法、数据抽象等核 心思想贯穿备个主题 t 并且充分展现了各主题的历史背罱、发展历程和新的枝术 趋势, 培养读者的大局观,为其今后深入 学习其他计算机专业课程打下坚实的基础9 本书深入浅出、图文并茂,内容引人入胜,极易引发读者的兴趣.绝 无一般 教材的枯燥和晦涩。此外.本书的教学手 段多样、习题丰富,并且每章后都附有与本章内容相关的社会现实问题供读者思考和讨论,这些都很好地体现了作者强调 培养学生分析问题能力的教学理念。 第11版新增了手持移动设备,特别是智能手机的相关内容,主要涉及第3章【操作系统 j 、第4章(组网丨、第6章 ( 编程语言)和第7章 f 软件工程) D 此外,书中还对软件所有权和责任、训练人工神经网络等内容做了更新,以反映最 新技术面貌。 J. Glenn Brookshear 世界知名的计算机科学教育家。他在1975年获得新墨西哥州立大学博±后,创办了 Marquette 大学的计算机科学学位项目,并在该校任教至今。他的主要研究方向是计算理论。除了本书之外,他还著有 T^eo/y of Computation: Format Languages, Automata, and Complexity^ a 计算机科学的全景式展现 _ 首屈一指的导论性教材 经典传承,新知荟萃 PEARSON www pearsonhighered.com wwwjtunngxom.cn 反馈投稿推荐信箱 : contact(gnuringbook.com 热线: (01 0>51095186转604 &算 机/计算机科学 人民邮电出版社 网址: www,ptpress com.cn ISBN 976 - 7 - 115 - 26196-0 PEARSON ^Education 9 787115 261960 iSBN 978-7-115-26196-0 定价 = 69.00 元 PTO s 灵计貿 in 斛莩 m 书 计算机科学概论 第11版 Computer Science An Overview [美】 J . Glenn Brookshear 著 刘艺肖成海马小会毛倩倩译 图书在版编目 (c I P ) 数据 计算机科学概论 :第 11版 /( 美)布鲁克希尔 ( Brookshear , J . G .) 著;刘艺等译.一北京:人民邮 电出版社, 201 L 10 (图 ,计算 机科学丛书) ^ 书名原文 : Computer Science : An Overview , Eleventh Edition ISBN 978-7-115-26196-0 I . ①计… II . ①布…②刘… III . ①计算机科学 IV ,① TP 3 中国版本图书馆 CIP 数据核字 (2011) 第174089号 内容提要 本书是计算机科学概论课程的经典教材,全书对计算机科学做了百科全书式的精彩阐述,充分展现 了计算机科学的历史背景、发展历程和新的技术趋势。本书首先介绍的是信息编码及计算机体系结构的 基本原理(第 1 章和第 2 章),进而讲述操作系统(第 3 章)和组网及因特网(第 4 章),接着探讨了算 法、程序设计语言及软件工程(第 5 章至第 7 章),然后讨论数据抽象和数据库(第 8 章和第 9 章)方面 的问题,第10章通过图形学讲述计算机技术的一些主要应用,第11章涉及人工智能,第12章通过对计 算理论的介绍来结束全书。本书在内容编排上由具体到抽象逐步推进,很适合教学安排,每一个主题自 然而然地引导出下一个主题。此外,书中还包含大量的图、表和示例,有助于读者对知识的了解与把握^ 本书适合用作高等院校计算机以及相关专业本科生的教材。 图灵计算机科学丛书 _ 计算机科学概论(第11版) _ ♦著 [美] J . Glenn Brookshear 译 刘艺肖成海马小会毛倩倩 责任编辑卢秀丽 执行编辑毛倩倩李静 ♦ 人民邮电出版社出版发行 北京市崇文区夕照寺街14号 邮编 100061 电子邮件31 5@ ptpress . com.cn 网址 http : //ww w . ptpress . com .cn 三河市海波印务有限公司印刷 ♦ 开本: 787 X 1092 1/16 印张: 26.5 字数: 786 千字 2011 年 10 月第 1 版 印数: 1-5 000 册 2011 年 10 月河北第 1 次印刷 著作权合同登记号 图字: 01-2011-2960 号 _ ISBN 978-7-115-26196-0 _ 定价: 69.00 元 读者服务热线: (010)51095186 转604印装质最 热线: (010)67129223 反盗版 热线: (010)67171154 版权声明 Authorized translation from the English language edition, entitled Computer Science: An Overview, Eleventh Edition , 978-0-13-256903-3 by J. Glenn Brookshear, published by Pearson Education, Inc., publishing as Addison Wesley, Copyright © 2012, 2009, 2007, 2005, 2003 by Pearson Education, Inc. All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from Pearson Education , Inc. CHINESE SIMPLIFIED language edition published by PEARSON EDUCATION ASIA LTD. and POSTS & TELECOM PRESS Copyright © 2011. 本书中文简体字版由 Pearson Education Asia Ltd. 授权人民邮电出版社独家出版。未经出版 者书面许可,不得以任何方式复制或抄袭本书内容。 本书封面贴有 Pearson Education ( 培生教育出版集团)激光防伪标签,无标签者不得销售。 版权所有,侵权必究。 刖 本书是计算机科学的入门教材。在力求保持学科广度的同时,还兼顾深度,以对所涉及的 主题给出中肯的评价。 \一•… t - t , - 丄 u t r L u 」' …"二> 了 ,、 Wt mn jw - .i , v ^ 「 ■丄: ■ l : - h \ - "n w , 一 ds w x t 在莕蘇趣疵織■碑观 读者对象 _ 本书面向计算机科学以及其他各个学科的学生。大多数计算机科学专业的学生在最初的学 习中都有这样一个误解,认为计算机科学就是程序设计、网页浏览以及因特网文件共享,因为 这基本上就是他们所看到的一切。实际上计算机科学远非如此。因此,在入门阶段,学生们需 要了解他们主攻的这门学科所涉及内容的广度,这也正是本书的宗旨。本书力图使学生对计 算机科学有一个总体的了解,希望在这个基础上,他们可以领会该领域今后其他课程的特点以 及相互关系。事实上,本书采用的综述方式也是自然科学入门教程的常用模式。 其他学科的学生如果想融入这个技术化社会,也需要具备这些宽泛的知识背景。适用于他 们的计算机科学课程提供的应该是对整个领域很实用的剖析,而不仅仅是培训学生如何上网和 使用一些流行的软件。当然这种培训也有其适用的地方,但本书的目的不在于此,而是用作计 算机科学的教科书。 因此,在写这本书时,我们力求保持其对非技术类学生的可读性。因此,先前的版本已经 被很成功地用作教科书,读者囊括了从高中生到研究生的各个教育层次众多专业的学生。这一 版仍将贯彻这一目标。 第11版新增的内容 ___ 第11版主要增加关于手持移动设备的内容,特别是智能手机。因此,这一版的内容经过了 修改、扩充,目的便是呈现所述主题与智能手机技术之间的关系。具体主题 包括: □ 智能手机硬件; □ 3 G 网络与 4 G 网络的 区别; □ 智能手机操作 系统; □ 智能手机软件 开发; a 智能手机的人机交互界面。 以上新增内容主要出现在第3章(操作系统)和第4章(组网),第6章(编程语言)和第7章 (软件工程)中也有提及。 这一版中的其他明显变动包括对以下内容的更新。 □ 软件所有权和 责任: 这一版重写并更新了第7章(软件工程)中与此主题相关的内容。 □ 训练人工神经 网络: 相关内容(见第11章,人工智能)已经实现了现代化。 最后,这一版更新了全书内容来反应当今的技术状况。这主要体现在第0章(绪论)、第1 章(数据存储)和第2章(数据操作)。 章节安排 ___ 本书主题由具体到抽象逐步推进——这是一种很利于教学的顺序,每一个主题自然而然地 引导出下一个主题。首先介绍的是信息编码、数据存储及计算机体系结构的基本原理(第1章和 第2章),进而是操作系统(第3章)和计算机网络(第4章),接着探讨了算法、程序设计语言及 软件开发(第5章至第7章),然后探索如何更好地访问信息(第8章和第9章),第10章讲述计算机 图形学技术的一些重要应用,第11章涉及人工智能,第12章通过对计算理论的介绍来结束全书。 本书编排顺序自然连贯,但各个章节具有很强的独立性,可以单独查阅,也可以根据不同 学习顺序重新排列。事实上,本书通常作为各类课程的教材,内容选择的顺序是多种多样的。 其中一种教法是先介绍第5章和第6章(算法和程序设计语言),然后按照需要返回到前面相应章 节。我还知道有人是从第12章有关可计算性的内容开始的^这本书还曾作为深入不同领域项目的 基础,用于“高级研讨班”的教科书。对于不需要了解太多技术的学生,教学中可以重点讲述 第4章(组网及因特网)、第9章(数据库系统)、第10章(计算机图形学)和第11章(人工智能)。 每章开篇都用星号标出了选学章节。选学章节要么是讨论更专业的话题,要么是对传统内 容作深入探究。此举仅仅是为那些想釆取不同阅读顺序的人提供一点建议。当然,还有其他读 法。尤其对于那些寻求快速阅读的读者,我建议采取下面的阅读顺序。 早 TJ 主 题 1■卜 1.4 数据编码和存储基础 2.1—2.3 计算机体系结构和机器语言 3.1—3.3 操作系统 4.1 〜 4.3 组网及因特网 5.1 〜 5.4 算法和算法设计 6.1—6.4 程序设计语言 7.1—7.2 软件工程 . 8.1—8.3 数据抽象 9.1—9.2 数据库系统 10.1 — 10.2 计算机图形学 11.1 — 11.3 人工智能 12.1 — 12.2 计算理论 在本书中有几条贯穿始终的主线。主线之一是计算机科学是不断发展变化的。本书从历史 发展的角度反复呈现各个主题,讨论其当前的状况,并指出研究方向。另一条主线是抽象的作 用以及用抽象工具控制复杂性的方式。该主线在第0章引入,然后在操作系统、体系结构、组网、 算法、程序设计语言、软件工程、数据组织和计算机图形学等内容中反复体现。 致教师 本教材所包含的内容很难在一个学期内讲授完,因此一定要杲断地砍掉不适合自己教学目 标的那些主题,或者根据需要重新调整讲授顺序。你会发现,尽管本书有它固有的结构体系, 但各个主题在很大程度上是相对独立的,完全可以根据需要作出选择。我写本书的目的是把它 VI 前言 作为一种课程的参考书,而非圈定课程的内容。我希望你把某些主题留作阅读作业,鼓励学生自 己学习,而不在课堂讲授。如果我们认为所有的东西都一定要在课堂上讲,那就低估学生的能力 了。我们应该教会他们独立学习。 关于本书从具体到抽象的组织结构,我觉得有必要多言几句。作为学者,我们总以为学生 会欣赏我们对于学科的观点,这些观点通常是我们在某一领域多年工作中形成的。但作为老师, 我认为最好从学生的视角呈现教材。这就是为什么本书首先介绍数据的表示/存储、计算机体系 结构、操作系统以及组网,因为这些都是学生们最容易产生共鸣的主题——他们很可能听说过 JPEG 、 MP 3 这些术语,可能用 CD 和 DVD 刻录过资料,买过计算机配件,应用过某一操作系统, 或者上过因特网。我发现,从这些主题开始讲授这门课程,学生可以为许多困惑他们多年的问 题找到答案,并且把这门课看做是实践课程而不是纯理论的课程。由此出发就会很自然地过渡 到较抽象的内容上,例如算法、算法结构、程序设计语言、软件开发方法、可计算性以及复杂 性等,而这些内容就是我们本领域的人所认为的计算机科学的主要内容。正如我前面所说的, 我并不是强求大家都按此顺序讲课,只是鼓励你们如此尝试一下。 我们都知道,学生能学到的东西要远远多于我们直接传授的内容,而且潜移默化传授的 知识更容易被吸收。当要“传授”问题的解决方法时,就更是如此。学生不可能通过学习问 题求解的方法变成问题的解决者,他们只有通过解决问题——还不仅仅是那些精心设计过的 “教科书式的问题”,才能成为问题的解决者。因此我在本书中加入了大量的问题,并特意让 其中一些问题模棱两可——意味着正确方法或正确答案可能不只一个。我建议你们采用并充 分拓展这些问题。 另一类适合“潜移默化学习”的主题还有职业精神、伦理和社会责任感。我认为这种内容 不应该独立成章,而是应该在有所涉及时讨论,而这正是本书的编排方法。你们会发现, 3.5 节、 4.5 节、7,8节、 9.7 节和 1 L 7 节分别在操作系统、组网、软件工程、数据库系统和人工智能的上下 文中提及了安全、隐私、责任和社会意识的问题。此外, 0.6 节就通过总结一些比较著名的理论 而引入这一主题-这些理论都企图把伦理上的决断建立在哲学的坚实基础上。你还会发现, 每一章都包含了 “社会问题”小节,这些问题将鼓励学生思考现实社会与教材内容的关系。 感谢你对本书感兴趣。无论你是否选用本书作为教材,我都希望你认同它是一部好的计算 机科学教育文献。 教学特色 本书是多年教学经验的结晶,因此在辅助教学方面考虑较多。最主要的是提供了丰富的问 题以加强学生的参与-这一版包含1000多个问题,分为“问题与练习”、“复习题”和“社会 问题”。“问题与练习”列在每节末尾(除了第0章外),用于复习刚刚讨论过的内容、扩充以前 讨论过的知识,或者提示以后会涉及的有关主题。这些问题的答案可以从图灵社区 ( www . itiiring . com . cn ) 本书网页免费注册下载。 “复习题”列在每章的末尾(第0章除外)。它们是课后作业,内容覆盖整章,在书中不给出 答案。 “社会问题”也列在每章的末尾,供思考讨论。许多问题可以用来开展课外研究,可要求学 生提交简短的书面或口头报告。 在每章的末尾还设有“课外阅读”,它列出了与本章主题有关的参考资料。同时,前言以及 正文中所列的网址也非常适合查找相关资料。 补充材料 本书的许多补充材料可以从配套网站 www . pearsonhighered . com / brookshear 上找到。以下内 容面向所有读者。 □ 每章的实践项目帮助加深理解本教材的主题,并可以帮助了解其他相关主题。 □ 每章的“自测题”帮助读者复习本书中的内容。 □ 介绍 Java 和 C 杆基本原理的手册,它在教学顺序上与本书是兼容的。 除此之外,教师还司以登录 Pearson Education 的教师资源中心 ( www . pearsonhighered . com ) 网站申请获得下面的教辅资料。 □ 包含“复习题”答案 ® 的教师指南。 □ PowerPoint 幻灯片讲稿。 □ 测试题库。 你也许还想看一下我的个人网站 www . mscs . mu . edu /~ glennb , 不是很正式(体现了我某一 时的灵感和幽默),但你或许能找到些有用的信息。特别值得一提的是,你将在此找到本书的 勘误。 致学生 ___ 我有^一 '点 点偏执(一些朋友说我可逃不是一点点),所以写本书时,我很少接受他人的建议, 其中许多人认为一些内容对于初学者过于高深。我相信即使学术界把它们归为“高级论题”,但 只要与主题相关就是合适的。读者需要的是一本全面介绍计算机科学的教科书,而不是“缩水” 的版本 只包括那些简化了的、被认为适合初学者的主题。因此我不回避任何主题,而是力 求寻找更好的解释。我力图在一定深度上向读者展示计算机科学最真实的一面。就好比对待菜 谱里的那些调味品一样,你可以有选择地略过本书的一些主题,但我全部呈现出来是为了在你 想要的时候供你“品尝”,而且我也鼓励你们去尝试。 我还要指出的是,在任何与技术有关的课程中,当前学到的详细知识未必就适合以后的需要。 这个领域是发展变化的 这正是使人兴奋的方面。本书将从现实及历史的角度展现本学科的内 容。有了这些背景知识,你们就会和技术一起成长。我希望你们现在就开始行动起来,不要局限 于课本的内容,而要进行大胆探索。要学会学习。 r 感谢你们的信任,选择了我的这本书。作为作者,我有责任创作出值得一读的作品。我希 望你们觉得我已经尽到了这份责任。 沌强⑽ ㈣ :孤 蝴拉似.财 绝赋從 ㈣㈣ 跑苑臨娜識部娜自 致齒 聊购■:』獻⑽獅 ..獅 iJ : 續卿:麵匕聊 i. ㈣ 啤成勒 此;; ;嫩 ㈣ 撕; 首先我要感谢那些支持本书(阅读并使用本书前几个版本)的人们,我感到很荣幸。 David T _ Smith (宾夕法尼亚州印第安纳大学)和 Dennis Brylow (马凯特大学)对这一版 的制作给予了很大帮助。 David 的贡献主要集中在第0章、第1章、第2章、第7章和第11章, Dennis 的贡献主要集中在第3章、第4章、第6章和第10章。如果没有他们的工作,这一版将不会出现 我衷心地感谢他们。 ①也可在图灵社区 ( www . ituring . com . cn ) 本书网页免费注册下载。-编者注 我在第 10 版的前言中已经提到过,要特别感谢 Ed Angel、John Carpinelli、Chris Fox、Jim Kurose、Gary Nutt、Greg Riccardi 和 Patrick Henry Winston 在第 10 版中所做的贡献。他们的努力 成果在第11版被保留了下来。 其他对这一版和之前版本作出贡献的人包括 J.M. Adams 、 C. M. Allen 、 D. C. S. Allison 、 R. Ashmore 、 B. Auemheimer、R Bankston 、 M. Barnard 、 P_ Bender 、 K. Bowyer 、 P. W. Brashear 、 C. M. Brown. H. M. Brown, B. Calloni 、 M. Clancy 、 R. T. Close 、 D. H. Cooley 、 L. D. Cornell 、 M. J. Crowley 、 F. Deek 、 M. Dickerson 、 M. J. Duncan 、 S. Ezekiel 、 S. Fox 、 N. E. Gibbs 、 J. D. Harris 、 D. Hascom 、 L. Heath> P. B. Henderson> L. Hunt 、 M. Hutchenreuther 、 L. A. Jehn 、 K. K. Kolberg 、 K. Korb 、 G. Krenz 、 J, Liu 、 T. J. Long 、 C. May 、 J. J. McConnell 、 W. McCown 、 S. J. MerrilK K. Messersmith. J. C. Moyer 、 M. Murphy 、 J, P. Myers 、 Jr. , D. S. Noonan. W. W. Oblitey 、 S. Olariu 、 G. Rice 、 N. Rickert 、 C. RiedeseK J. B. Rogers、G Saito 、 W. Savitch 、 R. Schlafly 、 J. C. Schlimmer. S. Sells 、 G. Sheppard. Z. Shen 、 J. C. Simms 、 M_ C. Slattery, J. Slimick 、 J. A. Slomka 、 D. Smith 、 J. Solderitsch 、 R. Steigerwald 、 L. Steinberg. C. A. Struble 、 C. L. Struble 、 W. J. Taffe 、 J. Talburt. P_ Tonellato 、 P. Tromovitch 、 E. D , Winter 、 E. Wright 、 M. Ziegler, 还有一位匿名的朋友 D 我向他 们中的每一位致以最真诚的谢意。 如前所述,本书的配套网站上有 Java 和 C++ 手册来讲述这两种语言的基础知识,与本书的 内容相得益彰。它们是由 Diane Christie 撰写的,在此表示感谢 D 另外,还要感谢 Roger Eastman , 他是本书配套网站上每章实践项目的出题人。 我同时要感谢为本项目作出贡献的 Addison - Wesley 的员工。他们不仅是很好的合作伙伴, 而且是很好的朋友。如果你们打算写一本教材,可以考虑交给 Addison - Wesley 出版。 我还要感谢我的夫人 Earlene 和我的女儿 Cheryl , 感谢她们这么多年对我的鼓励。当然 Cheryl 已经长大,几年以前已经离家开始独自生活. Earlene 还陪在我身边。我是一个幸运的人。1998 年12月11日的早晨,我突发心脏病,是她及时把我送到了医院,让我逃过了一劫。(对于年轻一 代的你们,我有必要解释一下,躲过心脏病的一劫有点像你们又获准延期提交课后作业。) 最后,我要感谢我的父母,本书即是给他们的献礼。我用下面一句赞美的话作为结束,就 不说是他们哪一位说 的了: “我们儿子的书真的非常好,人人都应该阅读。” J. G. B. 目 录 第 0 章绪论 ... 1 0.1 算法的作用 . 1 0.2 计算机器的由来 . 3 0.3 算法的科学 . 7 0.4 抽象 ... 8 0.5 学习大纲 . 8 0.6 社会影响 . . . 9 社会 f 句题 . 11 课夕 F 阅读 . 12 第 1 章数据存储 . 13 1.1 位和位存储 . 13 1.1.1 布尔运算 . 13 1.1.2 门和触发器 . 14 1.1.3 十六进制记数法 . 17 1.2 主存储器 . 18 1.2.1 存储器结构 . 18 1.2.2 存储器容量的度量 . . 19 1.3 海量存储器 . 20 1.3.1 磁学系统 . 20 1.3.2 光学系统 . 22 1.3.3 闪存驱动器 . 23 1.3.4 文件存储及检索 . 24 1.4 用位模式表示信息 . 25 1.4.1 文本的表示 . 25 1.4.2 数值的表示 . 26 1.4.3 图像的表示 . 27 1.4.4 声 音的寿 . 28 *1.5 二进制系统 . 29 1.5.1 二进制记数法 . 29 1.5.2 二进制加法 . 31 1.5.3 二进制中的小数 . 32 *1.6 整数存储 . 33 1.6.1 二进制补码记数法 . 33 1.6.2 余码记数法 . 36 *1.7 小数的存储 . 37 1.7.1 浮点记数法 . 37 1.7.2 截断误差 . 39 *1.8 数据压缩 . 41 1.8.1 通用的数据压缩技术 . 41 1.8.2 图像压缩 . 43 1.8.3 音频和视频压缩 . 44 *1.9 通信差错 . 45 1.9.1 奇偶校验位 . 45 1.9.2 纠错编码 . 46 复习题 . 47 社会问题 . 50 课外阅读 . 51 第 2 章数据操控 . 52 2.1 计算机体系结构 . 52 2.1.1 出知识 . 52 2.1.2 存储程序概念 . 53 2.2 机器语言 . 54 2.2.1 指令系统 . 54 2.2.2 一种演示用的机器语言 . 56 2.3 程序执行 . 58 2.3.1 程序执行的一个例子 . 60 2.3.2 程序与数据 . 62 *2.4 算术 / 逻辑指令 . 63 2.4.1 逻辑运算 . 63 2.4.2 循环移位及移位运算 . 65 2.4.3 算术运算 . 66 *2.5 与其他设备通信 . 67 2.5.1 控制器的作用 . 67 2.5.2 直接内存存取 . 68 2.53 握手 . 69 2.5.4 流行的通信媒介 . 69 2.5.5 通信速率 . 70 *2.6 其他体系结构 . 70 2.6.1 流水线… . 70 2.6.2 多处理器计算机 . 71 复习题 . 72 社会问题 . 77 X 目 录 课外阅读 . 77 第 3 章操作系统 . 79 3.1 操作系统的历史 . 79 3.2 操作系统的体系结构 . 82 3.2.1 软件概述 . 82 3.2*2 搮作系统组件 . 84 3.2.3 系统启动 . 86 3.3 协调机器的活动 . 88 3.3.1 进程的概念 . 88 3.3.2 进程管理 . 88 *3.4 处理进程间的竞争 . 90 3.4.1 信号量 . 90 3.4.2 死锁 . 91 3.5 安全性 . 93 3.5.1 来自机器外部的攻击 . 93 3.5.2 来自机器内部的攻击 . 94 复习题 . 95 社会问题 . 98 课外阅读 . 98 第 4 章组网及因特网 . 99 4.1 网络基础 . 99 4.1.1 网络分类 . 99 4.1.2 协议 . 100 4.1.3 网络互连 . 102 4.1.4 进程间通信的方法 . 104 4.1.5 分布式系统 . 105 4.2 因特网 . 106 4.2.1 因特网体系结构 . 106 4.2.2 因特网编址 . 108 4.2.3 因特网应用 . 109 4.3 万维网 . 113 4.3.1 万维网实现 . 113 4.3.2 HTML . 114 4.3.3 XML . 117 4.3.4 客户端和服务器端的活动 . 118 *4.4 因特网协议 . 119 . 4.4.1 因特网软件的分层方法 . 119 4.4.2 TCP/IP 协议簇 . 122 4.5 安全性 . 123 4.5.1 入侵的形式 . 124 4.5.2 防护和对策 . 125 4.5.3 力口密 . 126 4.5.4 网络安全的法律途径 . 128 复习题 . 130 社会问题 . 131 课外阅读 . 132 第 5 章算法 . 134 5.1 算法的概念 . 134 5.1.1 概览 . 134 5.1.2 算法的正式定义.… . 135 5.1.3 算法的抽象本质 . 136 5.2 算法的表示 . 136 5.2.1 原语 . 137 5.2.2 伪代码 . 139 5.3 算法的发现 . 142 5.3.1 问题求解的艺术 . 142 5.3.2 入门 . 144 5.4 迭代结构 . 146 5.4.1 顺序搜索法 . 147 5.4.2 循环控制 . 148 5.4.3 插入排序算法 . 151 5.5 递归结构 . 154 5.5.1 二分搜索算法 . 154 5.5.2 递归控制 . 159 5.6 有效性和正确性 . 160 5.6.1 算法有效性 . 160 5.6.2 软件验证 . 163 复习题 .. 167 社会问题 . 171 课外阅读 . 171 第6章程序设计语言 . 172 6,1 历史回顾 .■•■172 6.1.1 早期程序设计语言 . 172 6.1.2 独立并超越机器 . 174 6.1.3 程序设计范型 . 175 6.2 传统的程序设计概念 . 179 6.2.1 变量和数据类型 . 180 6.2.2 数据结构 . 181 6.2.3 常量和字面量 . 182 6.2.4 赋值语句 . 183 6.2.5 控制语句 . 184 6.2.6 注释 . 187 6.3 过程单元 . 188 6.3.1 过程 . 188 6.3.2 参数 . 189 6.3.3 函数 . 192 6.4 语言实现 . 193 6.4.1 翻译过程 . 193 目录 XI 6.4.2 软件开发包 . 198 6.5 面向对象程序设计 . 199 6.5.1 类和对象 . 199 6.5.2 构造器 . 202 6.5.3 附加特性 . 202 *6.6 程序设计中的并发活动 . 204 *6.7 说明性程序设计 . 206 6.7.1 逻辑推演 . 206 6.7.2 Prolog . 208 复习题. . 210 社会问题 . 213 课外阅读 . 214 第 7 章软件工程 . 215 7.1 软件工程学科 . 215 7.2 软件生命周期 . 217 7.2.1 周期是个整体 . 217 7.2.2 传统的开发阶段 . 218 7.3 软件工程方法 . 220 7.4 模块化 . 221 7.4.1 模块式实现 . 222 7.4.2 搞合 . 224 7.4.3 内聚 .. 225 7.4.4 信息隐藏 . 225 7.4.5 构件 . 226 7-5 行业工具 . 227 7.5.1 较老的工具 . 227 7.5.2 统一建模语言 . 228 7.5.3 设计模式 . 232 7.6 质量保证 . 233 7.6.1 质量保证的范围 . 233 7.6.2 软件测试 . 234 7.7 文档编制 . 235 7.8 人机界面 . 236 7.9 软件所有权和责任 . 238 复习题 . 240 社会问题 . 242 课外阅读 . 243 第 8 章数据抽象 . 244 8.1 数据结构基础 . 244 8.1.1 数组 . 244 8.1.2 列表、栈和队列 . 245 8.1.3 树 . 245 8.2 相关概念 . 247 8.2.1 抽象 . 247 8.2.2 静态结构与动态结构 . 247 8.2.3 指针 . 248 83 数据结构的实现 . 248 8.3.1 数组的存储 . 248 8.3.2 列表的存储 . 251 8.3.3 栈和队列的存储 . 254 8.3.4 二叉树的存储 . 255 8.3.5 数据结构的操作 . 257 8.4 —个简短案例 . 259 8.5 定制的数据类型 . 263 8.5.1 用户自定义数据类型 . 263 8.5,2 抽象数据类型 . 264 *8.6 类和对象 . 266 *8.7 机器语言中的指针 . 267 复习题 . 269 社会问题 . 273 课外阅读 . 274 第 9 章数据库系统 . 275 9.1 数据库基础 .. 275 9.1.1 数据库系统的重要性 . 275 9.1.2 模式的作用 . 276 9丄3 数据库管理系统 . 277 9.1.4 数椐库模型 . 278 9.2 关系模型 .. 279 9.2.1 关系设计中 的问题 . 279 9.2.2 关系运算 . 282 9.2.3 SQL . 285 *9.3 面向对象数据库 . 287 *9.4 维护数据库的完整性 . 289 9.4.1 提交/回滚协议 . 289 9.4.2 锁定 . 290 *9.5 传统的文件结构 . 291 9.5.1 顺序文件 . 291 9.5.2 索引文件 . 294 9.5.3 散列文件 . 294 9.6 数据挖掘 . 297 9.7 数据库技术的社会影响 .. 299 复习题 . 300 社会问题 . 303 课外阅读 . 304 第 10 章计算机图形学 . 305 10.1 计算机图形学的范围 . 305 10.2 3 D 图形概述 . 307 10.3 建模 . 308 XII 目 录 10.3.1 单个物体的建模 . 308 10.3.2 整个场景的建模 . 313 10.4 渲染 . 314 10.4.1 光-表面交互 . 314 1 0,4.2 栽剪、扫描转换和隐藏面 的消除 . 316 10.4.3 着色 . 319 10.4,4 渲染-流水线硬件 . 320 *10.5 处理全局照明 . 321 10.5.1 光线跟踪 . 321 10.5.2 輻射度 . 323 10.6 动画 . 323 10.6.1 动画^出 . 323 10.6.2 运动学和动力学 . 325 10.6.3 动画制作过程 . 326 复习题 . 326 社会问题 . 328 课外阅读 . 329 第 11 章人工智能 . 330 11.1 智能与机器 . 330 11.1.1 智能体 . 330 1 U .2 研究方法 . 332 11.1.3 图灵测试 . 332 11.2 感知 . 333 11.2.1 理解图像 . 333 11.2.2 语言处理 . 335 11.3 推理 . 338 1 L 3.1 产生式系统 . 338 11.3.2 搜索树 . 340 11.3.3 启发式搜索 ... 342 11.4 其他研究领域 . 346 11.4.1 知识的表达和处理 . 346 11.4.2 学习 . 347 11.4.3 遗 传算法 . 349 11.5 人工神经网络 . 349 11.5.1 基本特性 . 350 11.5.2 训练人工神经网络 .--351 11.5.3 联想记忆 . 353 11.6 机器人学 . 356 11.7 后果的思考 . 358 复习题 . 359 社会问题 . 363 课外阅读 . 364 第12章计算理论 . 365 12.1 函数及其计算 . 365 12.2 图灵机 . 367 12.2.1 图灵机的原理 . 367 12.2.2 丘奇一图灵论题 . 369 12.3 通用程序设计语言 . 370 12.3.1 Bare Bones 语言 . 370 12.3.2 用 Bare Bones 语言编程 . 372 12.3.3 Bare Bones 的通用性 . 373 12.4 一个不可计算的函数 . 375 12.4.1 停机问题 . 375 12.4.2 停机问题的不可解性 . 376 12.5 问题的复杂性 . 379 12.5.1 问题复杂性的度量 . 379 12.5.2 多项式问题与非多项式 问题 . 382 12.5.3 NP 问题 . 383 *12.6 公钥密码学 . 386 12.6.1 模表示法 . 386 12.6.2 RSA 公钥加密系统 . 387 复习题 . 389 社会问题 . 392 课外阅读 . 392 附录 A ASCII 码…" . 394 附录 B 处理二进制补码表示的电路 . 395 附录 C —种简单的机器语言 . 397 附录 D 高级编程语言 . 399 附录 E 迭代结构与递归结构的等价性 .•…401 胃弓 I . 403 问题与练习答案 (图灵社区网站下载) 绪 论 t 开篇的这一章,我们探讨计算机科学所涉及的领域,介绍其历史背景,然后为我们的 ft 深入学习奠定基础。 本章内容 a;:’. ^ 響 ::, 0.2 计算机辱蝻由来 0.3 算法轉科学 0,4抽象 0.5 学习夫纲 0,6社舍影响 社会问题 课外阅读 计算机科学这门学科,是要为计算机设计、计算机程序设计、信息处理、问题的算法解决 方案和算法过程本身等主题建立科学的基础。计算机科学既是当今计算机应用的支柱,又是今 后计算基础设施的基础。 本书将详细介绍计算机科学,探索广阔的主题,包括构成大学计算机科学课程的大部分主 题。我们要领略这个领域的博大精深和变化发展。因此,除了这些主题本身,我们还关注它们 的历史发展、现今的研究动态以及今后的前景。我们的目标是让人们以学以致用的态度来对待 计算机科学——既帮助那些要在此领域继续深入学习的人,也促成其他领域的人在技术不断进 步的社会崭露头角。 0.1 算法的作用 首先让我们了解一下计算机科学最基础的概念——“算法”。一般来讲,算法 ( algorithm ) 是完成一项任务所遵循的一系列步骤。(在第5章,我们将给出比较精确的定义。)例如,有关于 烹饪的算法(称为菜谱),有在陌生城市准确定位的算法(通常称为道路指南),有使用洗衣机 的算法(通常标示在洗衣机的内盖上或者贴在自助洗衣店的墙上),有演奏音乐的算法(以乐谱 的形式表示),还有魔术表演的算法(见图0-1)。 在一台机器(如计算机)执行一项任务之前,必须先找到完成这项任务的算法,并且用与 该机器兼容的形式表示出来。某一个算法的表示称作一个程序 ( program )。 为了人们读写方便, 计算机程序通常打印在纸上或者显示在计算机屏幕上。为了便于机器识别,程序需要采取一种 与该机器技术兼容的形式进行编码。开发程序、采取与机器兼容的形式进行编码并将其输入到 机器中的过程,称作程序设计 ( programming )。 程序及其所表示的算法总称为软件 ( software ), 而机器设备本身则称为硬件 ( hardware )。 2 第 0 章绪 论 将这两个数中较大的一个和较小的一个分别赋予 M 和 iv 。 用 M 除以见余数设为/?。 如果 及不为 0,那么将 况 的值赋予 M , 并将/?的值赋予 7 V , 然后回到步骤2;否则最大公约数就是 7 V 当前被 赋予的值。 效果: 表演者从一副普通的扑克牌中抽取若干张牌,充分洗牌后将牌正面朝下展开在桌面上。然后,表演者会 根据观众的要求相应地翻出红牌或者黑牌。 秘诀: 步骤1从一副普通扑克牌中抽取10张红牌和10张黑牌。把它们根据颜色分为两摞,正面朝上放在桌面上。 步骤2告诉观众你己经选取了若干张红牌和黑牌。 步骤3拿起红牌,装作整理成一摞的样子,用左手正面朝下拿好牌,同时用右手的拇指和食指挤压这摞牌的 两端,把牌面向下推,使得每张牌呈现向下的弧形。然后,把这摞红牌扣在桌子上并 宣布: “这是其中 的红牌。” 步骤4拿起黑牌,模仿步骤3的方法,但要使这些牌呈现向上的弧形。然后,把牌扣在桌子上并宣布:“这是 其中的黑牌, 步骤5把黑牌放回桌面后,立即用双手把红牌和黑牌混在一起(仍然正面朝下),平铺在桌面上。告诉大家你 已经洗好了牌 D 步骤6只要桌面上还有扣着的牌,可以重复下面的 步骤: 6.1 请观众要一张红牌或黑牌; 6.2 如果所要的牌为红色,而且桌面上倒扣有凹形的牌,就翻幵其中的一张并告诉大家“这是一张 红牌”; 6.3 如果所要的牌为黑色,而且桌面上倒扣有凸形的牌,就翻开其中的一张并告诉大家“这是一张 黑牌”; 6.4 否则,就告诉大家桌面上没有所要求颜色的牌了,然后翻开桌面上所有的牌,以证实你的断言。 _ | 图 0-1 —个魔术的算法 算法的研究起源于数学学科。事实也的确如此,它是数学家的重要活动,远远早于当今计 算机的出现。它的目标是找出一组指令,描述如何解决某一特定类型的所有问题。求解两个多 位数商的长除算法是早期研究中一个最著名的例子。另一个例子是古希腊数学家欧几里得发现 的欧几里得算法——求两个正整数的最大公约数(见图0-2)。 描述: 本算法假定输入是两个正整数,目的是要计算这两个数的最大公约数。 图 0-2 求两个正整数的最大公约数的欧几里得算法 一旦我们找到了执行一个任务的算法,那么在执行该任务时,就不再需要了解该算法所依 据的原理——任务的完成演变成了遵照指令操作的过程。(不需要了解算法的工作原理,我们就 可以根据长除算法求商,或者根据欧几里得算法求得最大公约数。)在某种意义上,解决这个问 题的智能被编码到算法中。 我们能够设计出那些执行有用任务的机器,是因为我们有上述能力通过算法来捕获和传达 智能(至少是智能行为)。因此,机器的智能级别受限于算法所传达的智能。只有存在执行某一 项任务的算法时,我们才可以制造出执行这一任务的机器,换言之,如果我们找不到一个解决 某问题的算法,那么这个问题的解决就超出了机器的能力 范围。 20世纪30年代,库尔特•哥德尔 ( KurtGSdel ) 发表了不完备性定理的论文,它使确定算法 能力的局限性成为数学的一个研究课题。这个定理的主旨就是,在任何一个包括传统意义的算 术系统的数学理论内,总有一些命题的真伪无法通过算法的手段来确定。简言之,对于我们算 术系统的任何全面研究都超越了算法活动的能力。 fi : 骤骤骤 过步步步 0.2 计算机器的由来 3 这一认识动摇了数学的基础,于是关于算法能力的研究随之而来,它开创了今天计算机科 学这门学科。的确,正是算法的研究构成了计算机科学的核心。 0.2 计算机器的由来 今天的计算机有着庞大久远的世系渊源,其中较早的一个计算设备是算盘。历史告诉我们, 它很可能源于中国古代且曾被用于早期希腊和罗马文明。算盘本身非常简单,一个矩形框里固 定着一组小棍,而每个小棍上又各串有一组珠子(见图0-3)。在小棍上,珠子上下移动的位置 就表示所存储的值。正是这些珠子的位置代表了这台“计算机”所表示和存储的数据。这台机 器是依靠人的操作来控制算法执行的。因此,算盘自身只算得上一个数据存储系统,它必须在 人的配合下才成为一台完整的计算机器。 图 0-3 算盘 (Wayne Chandler 拍摄) 从中世纪到近代,人们开始探求更复杂的计算机器。后来, 一 些发明人开始基于齿轮技术 设计计算机器。采用这种技术的发明家有法国的布莱斯•帕斯卡尔 (Blaise Pascal , 1623 一 1662)、 德国的戈特弗里德 • 烕尔赫尔姆 • 莱布尼茨 (Gottfried Wilhelm Leibniz , 1646— 1716) 和英 国的查尔斯 • 巴贝奇 (Charles Babbage , 1792— 1871) 等。这些机器利用齿轮的位置来表示数 据,要在规定齿轮初始位置的基础上机械地输入数据。帕斯卡尔和莱布尼茨的机器所计算的结 果是通过观察齿轮的最终位置得到的。而巴贝奇构想了这样一种机器,可以把计算的结果打印 在纸上,从而消除可能出现的誊写错误。 就执行算法的能力而言,我们可以看到这些机器在灵活性上的进步。帕斯卡尔的机器只是 为了执行加法而设计,因此必须将正确的步骤序列嵌入到机器结构本身。同样,莱布尼茨的机 器也把算法嵌入在其体系结构中,但它提供了多种算术运算供操作员选择。巴贝奇的差分机仅 造了一个演示模型,可以被修改以执行各种计算,但他设计的分析机(因一直没有得到任何基 金支持而未生产出来)则能够在纸卡片上读取以洞孔形式表示的指令。所以,巴贝奇的分析机 是可编程的。事实上,奥古斯塔•艾达•拜伦 (Augusta Ada Byron ) 通常被称为世界上第一位 程序员,她曾发表过一篇论文,阐述巴贝奇的分析机如何编程并实现各种各样的计算。 讎懸 __ 纖麵 _1§1^^^^鍾賴1聽11^感81116_義1麟 ' :篇 •:色贝奇释 计的这 含机龢的瀚是瑰代计筹机巍计時▲攀,如舉雛.比较艋诛的 技水键逵 : 出屢洽机躁如果当时商典和竭府数据处理薷表速判 :令 秀的麵模,球么$爽奇娜 思想可能在 1.9 世纪就引发了计茸机苹今。 事 实上,在他有生名渾 > v 是&與了暴分机的策示 模璋。 该机器通过“逐次鼻分”的计算来决定蠢字值^我们来研竞一下计算整数平方的问题, 奉助我 们加深对这一技术的逑解。首先我们从基础知识开始, G 的平方龙0, J 的平方是1,,2 的:平方是4, _3钓平方是9。椐此,可以接照下雨的方法得到4的平方 '(MrM i ,首先 我们来 计算一下已知平方之间的差:1 2 -0 2 -1, 2 2 -1 2 -3, 3^2 2 =5 a 然后广戒射计:算这些结果的 差: 3 -1=2, 5 -3 #2。注意看 ,: 这些差都是2。德敢这个规律韻够成立(教學上可以证明 “它是成立的),那么我们可以得出填祥的, 结论: (4 2 -3 2 )和(3 2 -:麥)之间的差也一:定是2。 :「因为(4 2 -3 2 )'比(3匕2 2 )大2,所以4 2 -3 2 =々,4 2 = 3 2 +7= 1:6,现在,赛们已经知遒了4的 平方,那么就可以依播 P 、 2 2 、3 2 和4 2 的值继:续计算5的平方 。 (:,虽然4:;蘇;? sifc 讨论逐衣 差會已 -经超出了本书范围,但是学过微舞命的学生可能已观察到匕讀與 守襄 基 f 这样的事 实: y = 彳的二阶專数是一条直线, ) - '今々 'U ’ ' ■ " 1 - … 1 r 丨 r 卜 H I " lL 1 . - T - 卜, .............. - ' . . ._ .:,... .. : ..•.••■ i . .. • ... : i . ... . ..;. . ..! •••.:•• : ••" ! •; ' :- •' •'•••• - ■• .. : ' ' •-•:.:•:.' .. :. - .:• :'•:.; . ..; : . .•^ •.•• rV .••••... - .;...:.;:• ': l \ .•••••. . !' ! ••- !•'•••'.•;:•.':! '•••'H J -K : ^ KHv :^: - L, -: ,1, -::■-•. ■':•—..:• •: . LJ . ■. •- - i .^: -•>•:••. - .•■:• : .• . . !'. : • |.屮 . J ._. 一.,-人 ... … ' H ■ _ 1 - ] 「 y ~ .■.. hJ| _ 、 ' ^... n . ri ..' [;' L J H ^ L { /_ V ; r " T - 1 _ ■ 」 x jf 2 —阶差分二阶差分丨 H H m 4 - L 4 • ► H ! I ' -r - t , r r , T , 'nrn , • / v T W ^ r ir .r; 5r . V ~ [ T/ % 「工 T : / . V * L 〜 j 厂、 r 1 " ,f r |H ) h O ^ :洛失莱斯槪爵夫人奥舌辦塔•霁达•丼伧是计算界蘇 注的焦 4入物 L 茸达 ,辩教 的一生: 去洪坤 遂不輿: ( mis — imx 她体弱一秦厂身:.处 f 艮讀楚美从 ifc 鱗森會,、还 . : 舞养參徕 。 尽管对广泛的,科学感兴趣,但:地还是专 注于政 _都究:。:〗妇3:感:: : A 赌汰查:杀:: 垮奇:螃矣分机#机濟芾后? u 她就蛛邊台机器迷住了 〆 A 把二篇 .盔 &:取:奇务析漸蹲^ 、“谂 A ■林条々:幽译为:英文,.'算是 k 袭平 对蚌 算机 科学听做的 贡献, 爲消 ★ 漆藏_4择_李:申、 二增加 y - 个龢:介匆該 机器妁 巍用' 并拇鰣了舛子说明该机霧如_藥;畛奐辄春勢 麥二 禅4每:名贝奇 对艾邊 k 拜儉:妗 x 择:十分热情,逵是因为俾希擎 S 文:^可以:帮•喪得:: 到祿金援麻,:以建造他的;合析机。」雜摩拜伦觀爵的女儿 9 艾达 t 拜给華嗜植,:表生:: 毒醃上也肴—定的关系巴鹰奇藏#也没肴.翱资士援助/但是波选 綺 1 来。入 嫩认为 i 貪餘录包令; 了滅一 批奸算咖程婷的例子。关于已贝奇对系遂;作奉嘀肴多 大,, 省究计禁板 历東 岭学者■一直争论不和 . 有些巧史学家认为:$與奇味歲涂童大货滅_另外 :一些人鲥认为巴戚 奇并藏 有帮到羑也从很大程度土来看反而是-^相傘 〶;; :龙论 如辞,:典古 .斯 I * 爲妻 '疗令辱_考专挺_ 傘 Mi 计幕_序员:華,降酵 部为^ 為太的 :女镇,’廨滿♦命名 辦#^谱言 \( A 、• - -v :,- •:; 0.3 算法的科学 数据存储容量有限,程序设计过程复杂而耗时,诸如此类原因限制了早期计算机器所能处 理的算法复杂性。如今,随着这些局限性的消除,机器已经被应用到执行越来越艰巨、越来越 复杂的任务中。人们企图用算法表达这些任务,但却感到了思维能力上的不足,于是越来越多 的工作转向算法和程序设计过程的研究。 在这种情况下,数学家的理论研究开始有了回报。由于哥德尔不完备性定理,数学家已经 在研究有关算法过程的问题了,而这正是先进技术目前面临的问题。由此,孕育出了被称作计 算机科学 的这门 学科。 如今,计算机科学已经奠定了它算法科学的地位。这门科学范围很广,涉及数学、工程学、心 理学、生物学、商业管理和语言学等多个学科。事实上,研究计算机科学不同分支的研究人员对 科学的定义也许会截然不同。例如,计算机体系结构领域中的研究者主要关注微型电路技术, 因此他们将计算机科学视为技术的进步和 应用; 但数据库系统领域的研究者则认为计算机科学 就是要寻求方法来提升信息系统的有 用性; 而人工智能领域的研究者则把计算机科学视为智能 和智能行为的研究。 因此,介绍计算机科学必然要包含多个主题,接下来的章节都将始终遵循这种做法。对每 一个主题,我们的目标就是要介绍这门学科的核心思想、当前的研究课题以及一些用于本领域 中先进知识的技术。在学习过程中,我们很容易因主题太多而忽视整体框架。因此,为了整理 思路,我们现在提出一些问题,让大家看到该领域的研究焦点。 □ 算法过程可以解决什么样的问题? □ 怎样才能比较容易地找到算法? □ 如何改进表示和传达算法的技术? □ 如何分析和比较不同算法的特征? a 如何使用算法来操作信息? □ 如何应用算法来产生智能行、为? □ 算法的应用对社会有何种影响? 注意,所有这些问题都与算法研究有关(见图0-5)。 算法的肩 限性 算麵纖 算法的分析 算細偷 算法的传达 _的爰现 算法的表示 图 0-5 算法在计算机科学中的核心地位 8 第 0 章绪 论 0.4 抽象 抽象概念贯穿计算机科学的研究和计算机系统的设计,因此有必要在绪论中简单介绍。抽 象 ( abstraction ) 在本书中的意思是指一个实体的外部特征与其内部构成细节之间的分离。抽象 使我们可以忽略一些复杂设备(如计算机、汽车和微波炉等)的内部细节,而把它们作为单一 的可理解的单元,而且正是通过抽象,这些复杂的系统才能够被设计和生产出来。计算机、汽 车和微波炉由若干部件构成,而这些部件又分别由更小的部件构成。每个部件表示一层抽象, 在此层面上,该部件的使用与它内部构成细节是分隔的。 运用抽象,我们能够构造、分析和管理大型的复杂计算机系统,但如果从细节的层面上看 问题,就会使人不识庐山真面目。在每一个抽象层面上,我们都把此系统看成是由若干称为抽 象工具 (abstract tool ) 的部件组成的,而暂时忽略这些部件的内部构成。这样我们的精力就集 中了,可以考虑一个部件如何与同一层面其他部件发生作用,以及这些部件如何作为一个整体 形成更高级别的部件。由此我们就可以理解该系统中与手头任务有关的那部分,而不会在众多 的细节中迷失方向。 需要强调的是,抽象并不局限于科学和技术领域,它是一门重要的简化技术,我们的社会 所形成的任何一种生活方式都离不开抽象。很少有人知道,日常生活中各种各样的便利是怎样 实 现的: 我们需要吃饭穿衣,但却不能都自己 生产; 我们使用电器设备和通信系统,但不需要 了解它们的内部 技术; 我们享受其他人提供的服务,但不需要知道他们的专业细节。对每一项 新的发展只有一小部分社会成员专职于其实现,其他人则将实现的结果作为抽象工具来使用。 这样,社会的抽象±具仓库扩大了,社会进一步发展的能力也增强了。 抽象这一话题在本书中会被反复提及。我们将了解到,计算设备是通过各种抽象工具构建 的。我们还会看到,大型软件系统开发是以模块化方式完成的,其中每个模块都被作为较大模 块上的一种抽象工具。此外,在计算机科学本身的发展中,抽象也扮演了很重要的角色,有了 它,研究人员可以把精力集中在一个复杂领域中的特定范围。实际上,本书的编排也反映了该 科学的这种特征——每一章都围绕着计算机科学一个特定的范围,而且往往出人意料地完全独 立于其他各章,但所有这些章合在一起又形成了对该科学所涉及领域的全面介绍。 0.5 学习大纲 本书遵循自底向上的方法讲述计算机科学,先从读者有亲身体验的主题开始(比如计算机 硬件),继而引出比较抽象的主题(比如算法复杂性和可计算性)。结果是我们的学习遵循了这 样一个 模式: 随着对主题的深入理解,我们构建了规模越来越大的抽象工具。 我们首先学习的主题与设计和构造执行算法的机器有关。第1章(数据存储)学习现代计算 机的信息编码和信息存储问题,第2章(数据操控)研究简单计算机的内部基本操作。虽然部分 学习内容涉及技术问题,但总体上是独立于具体技术的。也就是说,像数字电路设计、数据编 码与压缩系统,以及计算机体系结构这样的话题在更广阔的技术领域中都很重要,并且不管技 术发展方向如何,它们的重要性都不会降低. 在第3章(操作系统)中,我们将学习控制一台计算机总体操作的软件,这种软件称为操作 系统。操作系统控制计算机与其外部世界之间的 接口: 保护计算机及其内部所存储的数据,以 免被非授权用户 访问; 允许计算机用户请求执行各种 程序; 协调内部活动,以满足用户请求。 在第4章(组网及因特网)中,我们将学习如何连接计算机以构成计算机网络,以及网络是 如何连接成互联网的。由此引出诸如网络协议、因特网结构和内部操作、万维网,以及诸多安 0.6 社会影响 9 全性问题等主题。 第5章(算法)比较规范地介绍了算法。我们要研究算法的发现,明确几种基本的算法结构, 开发几项表示算法的初等技术,并介绍算法的有效性和正确性问题。 第6章(程序设计语言)研究的问题是算法衾示和程序开发过程。我们会发现,人们在不断 改善程序设计技术的过程中,已经创造出各种各样的程序设计方法学或范式,而每一种都有自 己的一套程序设计语言。我们将研究这些范式和语言,以及语法和语言翻译的问题。 第7章(软件工程)介绍计算机科学的一个分支——软件工程。软件工程处理的是开发大型 软件系统时所遇到的问题。大型软件系统的设计是一项复杂的任务,会遇到传统工程未涉及的许 多问题。因此,软件工程这一学科已经成为计算机科学中一个重要的研究领域,从工程、项目管 理、人力资源管理、编程语言设计乃至建筑学等学科中吸取了大量养分。 在接下来的两章中,我们将学习在计算机系统中组织数据的方法。第8章(数据抽象)介绍 传统上用于在计算机主存储器中组织数据的技术,然后探索数据抽象的演变发展,从原语的概 念一直到今天的面向对象式技术。第9章(数据库系统)介绍传统上用于在计算机海量存储器中 组织数据的方法,并研究如何实现非常大的复杂数据库系统。 在第10章(计算机图形学)中,我们研究图形和动画,这是一个创建并图像化虚拟世界的 领域。由于像机器体系结构、算法设计、数据结构和软件工程等计算机科学传统领域的发展, 图形和动画学科取得了显著进展,业已发展成为激动人心、充满活力的学科。此外,这个领 域说明了计算机科学的各个组成部分是如何与物理、艺术和摄影术等学科相结合以产生显著 成果的。 在第11章(人工智能)中,我们将了解到,为了开发更有用的机器,计算机科学现已一马 当先,转向研究人类智能,希望通过对我们自己的思维推理和认知的了解,能设计出模拟这些 过程的算法,从而把这些能力传递给机器。结果是,计算机科学又诞生了一个称为人工智能的 领域, 它非常依赖于心理学、生物学和语言学等领域的研究。 我们的学习到第12章(计算理论)结束,在这一章中介绍了计算机科学的理论基础,这个 主题使我们了解了算法(和机器)的局限性。在本章,我们不但明确了几个算法上不能解决的 问题(它们在理论上也是超出机器能力的),而且认识到解决其他许多问题需要大量的时间或空 间,以致从实践的角度上讲也是不可解的。因此,我们能够领悟算法系统的应用范围和局限性。 我们的目标是,每一章都在一定深度上使读者真正理解所讨论的主题。我们希望所阐述的 计算机科学知识会对大家的工作有所帮助——使读者了解自己所生活的技术社会,打好跟随科 技进步自我学习的基础。 0.6 社会影 B 向 计算机科学的进步正淡化着许多差别,而这些差别正是我们过去作出某些决策的基准,而 且计算机科学的进步也向社会的许多准则提出了挑战.在法律上,因此产生了某些疑问——知 识产权的度以及伴随这个所有权的权利和义务 3 在伦理上,人们面临着许多挑战传统社会行为 准则的抉择。对于政府,又产生了许多争议——汁算机技术及其应用应该规范到什么程度?在 哲学上,人们开始争论智能行为的存在与智能本身的存在。同时,整个社会也在讨论:新的计 算机应用是代表新的自由还是新的控制? 虽然这些话题不属于计算机科学本身的范畴,但是对于那些想涉足计算机或者相关领域的 人,它们还是很重要的。科学新发现经常会使许多应用产生争议,这使得人们对相关的研究人 员产生极大不满。进一步而言,伦理上的过错足以摧毁本可以很成功的事业。 10 第 0 章绪 论 计算机技术的发展给人们提出了许多难题,而具备一些解决此类问题的能力对于非计算机 领域的人也十分重要。的确,计算机技术己经在全社会迅速普及,几乎无人不受其影响。 本书提供了一些技术背景,有助于人们以一种理智的思维处理计算机科学所产生的问题。 然而,计算机科学的技术知识本身无法提供全部问题的解决办法。因此,本书的一些章节致力 于介绍社会、伦理和法律上的问题,包括安全性、软件所有权和义务问题、数据库技术的社会 影响以及人工智能发展的后果。 此外,一个问题通常并不只有唯 一一 个正确的答案,许多有效的解决方案都是在对立的(也 许都是有理的)观点之间进行折中的。因此,寻找解决方案通常需要这样的 能力: 能够倾听、 辨别其他各种观点,开展理性的讨论,并在获得新的见解时改变自己的观点。于是,本书每章 最后都有一系列“社会问题”,研究计算机科学和社会的关系。这些问题不是必须作答的,而是 需要思考的。在许多情况下, 一 个乍看毫无疑问的答案等你发现其他可能性时就值得深思了。 简言之,给出这些问题并不是要指导大家找到“正确”答案,而是要提高大家的意识,要意识 到一个问题会牵扯多位利益相关者, 一 个问题会有多个解决方案,以及那些解决方案都同时具 有长短期效应。 在结论最后,我们介绍了一些伦理学方法,这些方法是哲学家在基础理论的研究中提出的, 从而产生了指导决策和行为的原则。这些理论大体可以归 类为: 结果伦理、职责伦理、合同伦 理以及基于性格的伦理。你也许希望用这些理论处理本书中呈现的伦理问题。特别值得一提的 是,你可能会发现不同的理论会导致相反的结论,从而将隐藏的候选方法呈现出来。 结果伦理试图基于作出各种选择所造成的后果分析问题。最突出的一个例子就是“功利主 义”——“正确”的决策或行动可以带给社会上大多数人最大利益。乍一看,功利主义似乎 很合理地解决了伦理上的难题,但是,它又会导致许多令人无法接受的后果。例如,他使少数 人要服从多数人。此外,很多人认为伦理理论的结果方法太过强调结果,这样人就仅仅被当 作实现结果的工具而不是有意义的个体了。他们还认为,这是所有结果伦理理论的一个最基本 的缺陷。 和结果伦理相反,职责伦理并不考虑决策和行动的结果,它认为社会成员本身应该有职责 或义务,因此又产生了需要解决的伦理问题。例如,一个人有尊重他人权利的义务,因此无论 后果如何,他都要反对奴隶制。另外,反对职责伦理的人认为,对于有争议的职责问题,它无 法提供解决方案。如果说出事实真相会使同事失去自信,你还会这样做吗?一个民族如果在战 争中自卫,那么在随后的战争中就会牺牲很多公民,这个民族还应该自卫吗? 合同伦理理论首先假设社会没有任何伦理根基。在这种纯天然的背景下,什么情况都可能 发生——每个人都必须自我保护,并不断防止他人的侵害。因此,合同伦理理论认为社会成员 之间应该建立“合同”。例如,你不剽窃我,我就不剽窃你。进而,这些“合同”就成为伦理习 惯的准绳。这里需要指出的是,合同伦理理论是伦理行为的动力,因为如果不遵循合同伦理我 们就将生活得很不愉快。然而,反对合同伦理理论的人认为,它不能为伦理难题的解决提供足 够广阔的基础,只有在那些已经建立合同的领域,它才能起到指导作用。(在没有合同约束的领 域,我就可以为所欲为。)特别值得一提的是,新技术可能发现人们未知的领域,在其中无法应 用现存的伦理合同。 性格伦理(有时称为德行伦理)是由柏拉图和亚里士多德提出的,它指的是“好行为”不 是运用各种规则的结果,而是“良好性格”的自然结果。当一个人解决伦理难题时,结果伦理、 职责伦理以及合同伦理认为应该考虑的分别是,“结果会怎样呢? ”“我的职责是什么呢? ”“我 有什么合同可作为依据呢?”而性格伦理考虑的是,“我想成为什么样的人呢?”因此,好行为 是建立在好性格基础上的,而这正得益于良好的教育以及德行习惯。 社会问题 11 向不同专业领域人士教授伦理知识时,一般以性格伦理为基础,即不用教授专门的伦理理 论,而是举一些案例,暴露专业领域的各种伦理问题。通过讨论这些案例的利弊,这些专业人士 就会对职业生活中潜在的危险有一个更清醒、更深入和更敏感的认识了,并将这种认识融入到 他们的性格中。这就是每章最后设计社会问题的精神所在。 社会问题 下面的问题有助于分析一些与计算领域相关的伦理、社会和法律问题。回答出这些问题还 不够,还应该考虑为什么这样回答,以及你的判断是否对每个问题都标准如一。 1. 如果没有计算机革命,我们现在的社佘将有很大不同。人们已经广泛接受这种观点。 现在的社会是更好,还是更差?如果你在社会中的地位不同,答案是否也会不同? 2. 不努力了解技术的基础知识,却又想积极参与到当今的技术社会中,这种做法是否可 行?例如,要通过表决来决定如何支持和使用某种技术,那么表决者是否有责任了解 那种技术?你的答案是否与具体哪种技术有关?例如,考虑使用核技术时和考虑使用 计算机技术时,回答是否一样? 3. 传统上人们选择现金交易方式处理账务,因而不需要支付服务费用。然而,我们经济 生活中自动化程度在不断提高,金融机构对使用某些自动化系统收取服务费用。那么, “服务收费不公正地限制了人们参与经济活动”这种说法是否正确呢?例如,假设雇主 仅用支票支付雇员的工资,并且所有金融结构都对支票兑现和存款收取服务费用,那么 雇员是否因此受到了不公正的待遇呢?如果雇主坚持通过直接存款的方式支付工资,那该 怎么办呢? 4. 在交互式电视节目中,某一个公司有可能会从孩子那里获取有关其家庭的信息(也许 是通过交互式游戏),那应该控制到什么程度呢?例如,是否可以允许公司通过孩子得 知其父母的购物习惯?那么关于孩子自己的信息呢? 5. 政府对计算机技术及其应用的法规管制应当到什么程度?例如,考虑一下问题3和问题 4中提到的问题。政府管制的依据是什么? 6. 关于技术,尤其是计算机技术,我们所作出的决策会对我们的后代有多大的影响? 7. 随着技术的进步,我们的教育系统不断面临挑战,要重新考虑科目安排的抽象层次。 许多问题是类似的,如某项技能是否必要,是否允许学生依赖某种抽象工具等。学三 角时,不再教学生如何利用函数表求三角函数的值,而是允许学生用计算器作为抽象 工具来求函数值。有些人认为,长除也应该让位于抽象。还有哪些主题涉及类似的争 论?现代的文字处理软件是否会使人们不需要练习书法?视频技术的使用是否会在将 来的某一天取代阅读? 8. 所有公民都有权获得信息,因而才设立了那么多公共图书馆。越来越多的信息通过计 算机技术存储和传播,是否每一位公民都应该有权利访问这个技术系统呢?答案如果 是肯定的,那么公共图书馆是否应该为这种访问提供渠道呢? 9. 在一个依靠抽象工具的社会里,会产生什么样的伦理问题呢?是否存在这样的情况, 当我们使用某个产品或某项服务时,不了解它们的工作原理,不了解其生产方法就有 悖道德?亦或不了解使用它会带来的副作用就有悖道德? 10. 随着我们社会的逐步自动化,政府监督公民的活动变得很容易。这是好还是坏呢? 11. George Orwell 在他的小说《1984》中想象的哪些技术已经实现?它们的使用方法是否 与 Orwell 预想的一样? 12 第 0 章绪 论 12. 如果你有一台时间机器,你想生活在哪一个历史阶段?有你想带去的现代技术吗?你 所选择的技术可以脱离其^ [也 技术而被你单独带走吗? 一项技术可以在多大程度上独立 于其他技术?防止温室效应,却又接受现代医疗,这两者相符吗? 13. 假如由于工作关系,你必须生活在另一种文化氛围中。你会按照自己的本土文化习惯 我行我素,还是会选择遵循所在地的异域生活习俗?对这个问题的回答,是否因为跟 穿衣打扮还是人权有关而不同呢?如果你是在本国生活,但需要处理各种外来文化冲 突,那你会坚持什么道德标准? 14. 在商务、通信和社交互动方面,社会是否已太过依赖于计算机应用?例如,如果长期 中断因特网或移动电话服务,会有什么后果? 15. 大多数智能手机都能够利用 GPS 识别韦机的位置。这样一来,相关的应用程序就可以 基于手机的当前位置提供与该位置相关的信息(如本地新闻、本地天气,或者附近的 商业机构)。然而,选些 GPS 功能却也可能支持其他应用将手机的位置广播给其他各方。 这样好吗?手机的位置(继而手机用户的位置)信息会被如何滥用呢? 16. 根据你对以上问题的回答,你打算支持 0.6 节中的哪一个伦理理论? 课外阅读 ______ Goldstine, J. J. The Computer from Pascal to von Neumann. Princeton: Princeton University Press, 1972. Kizza , J. M. Ethical and Social Issues in the Information Age, 3 rd ed. London: Springer-Verlag, 2007. Mollenlioff, C. R. Atanasoff: Forgotten Father of the Computer. Ames: Iowa State University Press, 1988. Neumann, P. G. Computer Related Risks. Boston, MA: Addison-Wesley, 1995. Ni, L. Smart Phone and Next Generation Mobile Computing. San Francisco: Morgan Kaufrmann, 2006. Quinn, M. J. Ethics for the Information Age, 2nd ed. Boston, MA: Addison-Wesley , 2006. Randell, B. The Origins of Digital Computers, 3 rd ed. New York: Springer-Verlag, 1982 , Spinello, R. A. and H. T. Tavani. Readings in CyberEthics, 2nd ed. Sudbury, MA: Jones and Bartlett, 2004. Swade, D. The Difference Engine. New York: Viking, 2000. Tavani, H. T. Ethics and Technology: Ethical Issues in an Age of Information and Communication Technology, 3rd ed. New York: Wiley, 2011. Woolley, B. The Bride of Science, Romance, Reason, and Byron’s Daughter. New York: McGraw-Hill, 1999. 数据存储 t 本章中,我们学习有关计算机中数据表示和数据存储的内容。我们要研究的数据类型 ■包括文本、数值、图像、音频和视频。除了传统计算外,本章的很多内容还涉及数字 摄影、音频/视频录制和复制,以及远程通信等领域。 本章内容 U 位和位存储 1.2 主存储器 1.3 海量存储器 1.4 用位4式表示信息 *1.5 二进制系统 *1.6 整数存储 : 输入> > ^ E > ■> 输出 2. 对于图 1-3 中的触发器,我 © 在文中强调 ,::下输入 端放置1 (保持 jh 输入端为 0) ,这样驗迫使触发器 的输出为0。,描述一下这砰情_发攝内:雖的:辱动:序列。: 当输入偉都为鄉 t , 输出值蠢0。轉非1:1的符夸相每口的符号类似,只 cfe 輪出有一下面的 电路包含与非 rt , 那么壤平电路完成释么布尔运算? :_ - _ : 18 第 1 章数据存储 iftA > 输入> 输出 b ; N 果 〔个贏门鉍 •出值传递_一本離 n ;那 X 这个&合电路 雲的泰 5^_棘兔或非,:当瓦仅当 输入值都为0时,输出值为 U 或非门的符号和或门的符号类似,只是输出有一个圆圈。.下面的电 路包含一个与门和两个或非门。那么这个 电路汁 算_为什么布尔运算? _人.:> =0 输出 tfA, >— ~~- • - 5. 用十六进制记数法来表示下面的位模式。 a; 0110101011110010 b. 111010000101010100Q10111 G. 010010Q0 6. If 國的十靠趣制模 或表滴 騎么隹模式:?. a* 5FD97 b. 6iOA c. ABCD d.0100 1.2 主存储器 为了存储数据,计算机包含大量的电路(如触发器),每一个电路能够存储单独的一个位。 这种位存储器被称为计算机的主 存储器 (main memory )。 1.2.1 存储器结构 计算机主存储器是以称为存 储单元 ( cell ) 的可管理单位组织起来的,一个典型的存储单元 容量是8位。[一个8位的串称为一 个字节 ( byte ), 因此一个典型的存储单元容量是一个字节。] 在像微波炉这样的家用电器中所使用的小型计算机的主存储器,仅仅包含几百个存储单元,但 是大型计算机的主存储器可能有上亿个存储单元。 虽然计算机中没有左或右的概念,但是我们通常假设存储单元的位是排成一行的。该行的 左端称为 高位端 ( high-order end ), 右端称为 低位端 ( low-order end )。 高位端的最左一位称作高 位 或最高有效位 (most significant bit )。 取这个名称是因为,如果把存储单元里的内容解释为数 值,那么这一位就是该数的最高有效数字。类似地,低位端的最右一位称为低位 或最低有效位 (least significant bit ). 于是,我们可以如图 1-7 所示的那样描述字节型存储单元的内容。 高位端 010.11010 低位端 最高 最低 有效位 有效位 图 1-7 字节型存储单元的结构 为了区分计算机主存储器中的各存储单元,每一个存储单元都被赋予了一个唯一的“名字”, 称 为地址 ( address )。 这类似于通过地址找到城市里的一座座房屋。不过,对于存储单元,所用 地址都是用数字表示的。更精确地说,我们把所有的存储单元都看做是排成一行的,并按照这 个顺序从0开始编号。这样的编址系统不仅为我们提供了唯一标识每个存储单元的方法,而且也 1.2 主存储器 19 给存储单元赋予了顺序的概念(见图1-8),这样就有了诸如“下一个单元”、“前一个单元”的 说法。 图 1-8 按地址排列的存储单元 将主存储器的存储单元和存储单元的位都进行排序,就产生一个重要结果,即计算机主存 储器的所有二进制位本质上被排成了一长行,因而这个长行上的片段就可以存储比单个存储单 元要长的位模式。特别是,我们只需要两个连续的存储单元就可以存储16位的串。 为了做成一台计算机的主存储器,实际存放二进制位的电路还组合了其他的电路,这些电 路使得其他电路可以在存储单元中存入和取出数据。以这种方式,其他电路可以通过电信号请 求从存储器中得到指定地址的内容(称为读操作),或者请求把某个位模式存放到指定地址的存 储单元里(称为写操作)。 因为计算机的主存储器由独立的、可编址的存储单元组成,所以可以根据需要独立访问这 些存储单元。为了反映用任何顺序访问存储单元的能力,计算机的主存储器常被称为 RAM (Random Access Memory , 随机存取存储器)。主存储器的这种随机存取特性与 1.3 节中将要讨论 的海量存储系统形成鲜明对比,在海量存储系统中长二进制串被作为合并块来操控。 尽管我们介绍说触发器可以作为二进制位的一种存储方法,但是在现代的大多数计算机中, RAM 都是用其他可以提供更小型化和更快响应时间的技术制造的,其中许多技术将位存储为可 快速消散的电荷。因此,这些设备需要附加的电路,称为刷新电路,可以在 Is 内反复补充电荷 很多次。因为它的这种不稳定性,所以通过这种技术构造的计算机存储器常被称为动态 存储器 (dynamic memory ), 于是就产生了术语 DRAM (读作 “ DEE - ram ”), 用来表示动态 RAM 。 有时 候关于动态存储器也会用 SDRAM (读作 “ ES - DEE - ram ”), 用来表示同步动态 RAM , 采取这种 附加的技术可以缩短从存储单元取出信息所需要的时间。 1.2.2 存储器容量的度量 在第2章会学到,如果主存储器中存储单元的总数是2的幂,那么设计起来是很方便的,因 此早期计算机存储器的大小通常以1024 (2 10 )个存储单元为度量单位。因为1024接近于数值 1000,所以计算机行业的许多人采用前缀千 ( kilo ) 来表示这个单位。也就是说,术语千 字节. ( kilobyte , 简写形式为 KB ) 用于表示1024字节。因此有4096个存储单元的计算机被称为有4 KB 存储器(4096=4 X 1024)。随着存储器容量的增大,又新增了一些类似的度量单位,包括 MB (兆字 节 )、 GB (吉字节 )、 TB (太字节)。遗憾的是,这种前缀用法属于术语的误用,因 20 第 1 章数据存储 为这些前缀已经是其他领域用于指称1000的幂。例如,在度量距离时千米 ( kilometer ) 指的是 1000米 ( m ), 在度量无线电频率时,兆赫 ( megahertz ) 指的是1 000 000赫兹 ( Hz )。 在此提醒 大家:一般说来,千 ( kilo -)、 兆 ( mega -) 等术语在涉及计算机存储器时表示2的幂,但在其他 环境中表示1000的幂。 问逾与练习 1. 如果地埤为5的存储单元春有值8,那么 在雜偉 5写入6号存储单元和将 | 号存储 单元的 移到 g 号存 储单元之间有什么差别?: 2 •假 定你想交换存俥在 2 号和 3 ,夸储单元中的值。,那么下两的步骤错在哪里? . 義: :卜、 ::: 步棘2把3考存储单元:中油4容^到2号存储单元。: ' 请设计能够正确交换这两个存储单元内容的步骤。如有必要可以使用额外的存储单元。 3. 4 KB 计算机存储器里有多少个二进制位? - 1.3 海量存储器 由于计算机主存储器的不稳定性和容量的限制,大多数计算机都有称为海量存储 (mass storage ) 系统的附加存储设备,包括磁盘、 CD 盘、 DVD 盘、磁带、闪存驱动器(所有这些我们 稍后会讨论)。相对于主存储器,海量存储系统的优点是更稳定、容量大、价格低,并且在许多 情况下可以针对存档的需要从计算机上方便地取下这类存储设备。 术语联机 ( on - line ) 和脱机 ( off - line ) 通常分别用来描述那些既能接入计算机又能从计算 机上移除的设备。联机意味着设备或信息己经与计算机连接,不需要人的干预就可以使用。脱机 意味着必须先有人的干预,设备和信息才可被计算机使用——或许需要先打开这个设备,或许需 要将包含该信息的介质插到某机械装置里。 海量存储系统的主要不足之处是,它们一般都需要机械运动。因为主存储器的所有工作都 是由电子器件实现的,所以比起计算机主存储器来,海量存储系统的数据存取需要花费更长 的时间。 1.3.1 磁学系统 很多年以来,磁技术己经占据了海量存储领域。最常见的例子便是我们今天使用的磁盘 (magnetic disk ), 它里面是薄的、可以旋转的盘片,表面有磁介质的涂层用以存储数据(图1-9)。 读/写磁头安装在盘片的上面和(或)下面,当盘片旋转时,每个磁头在盘片上面或下面相对于 称为磁道 ( track ) 的圆圈转动。移动磁头时,可以对各个同心的磁道进行存取。在很多情况下, 一个磁盘存储系统包含若干个安装在同一根轴上的盘片,层叠在一起,盘片之间留有足够的距 离,使得磁头可以在盘片之间滑动。这种情况下,所有的磁头是一起移动的。因此,每当磁头 移到新的位置时,就可以访问新的一组磁道,称为柱面 ( cylinder )。 因为一个磁道可以包含的数据通常比我们每一次要处理的数据多,所以每个磁道又被划分 成若干个小弧区,称为扇区 ( sector ) o 记录在每区上的信息是连续的二进制位串。磁盘上 所有的扇区包含相同数目的二进制位(典型的容量是512个字节到若干 KB ), 而且在最简单的磁 盘存储系统里,每一个磁道被分为相同数目的扇区。因此,盘片边缘磁道扇区上存储的位密度 要小于靠近盘片中心磁道上存储的位,这是因为外磁道要长于内磁道。事实上,在大容量磁盘 1.3 海量存储器 21 存储器系统里,边缘磁道可包含的扇区要远多于靠近中心的磁道,这种存储能力常通过一种称 作 ZBR (zoned-bit recording, 区位记录)的技术得以应用。运用 ZBR , 一 '些相邻的磁道被统一 命名为区,一个典型的盘片大约包含10个区。一个区的所有磁道有相同数目的扇区,但是靠外 的区中每一个磁道包含的扇区比靠内的区包含的多。因此,盘片边缘的存储空间利用率要高了。 不考虑细节,我们只需知道,一个磁盘存储系统包含许多独立的扇区,每一个扇区又可以作为 独立的位串进行存取 划分为扇区的磁道 磁盘 读/写磁头 一, ■ ■ 磁盘旋转方向 臂运动方向 图 1-9 磁盘存储系统 磁道和扇区的位置不是磁盘物理结构的固定部分,而是通过称为磁盘 格式化 ( formatting ) 的过程磁化形成的。这个过程通常是由磁盘的厂家完成的,出厂的此类盘称为格式化盘。大多 数计算机系统都能够执行此项任务。所以,如果一个磁盘的格式化信息被破坏了,那么可以重 新格式化这个磁盘,不过这种操作将会扔掉原先记录在磁盘上的所有信息。 一 个磁盘存储系统的容量取决于所用盘片数目以及所划分磁道与扇区的密度。较小容量的 系统可能只有一个盘片。大容量磁盘系统的容量可达数 GB , 甚至 TB , 可能在同一根轴上安装 有3〜6个盘片。此外,数据有可能存储在每个盘片上下两面。 有几个标准可以用来评估一个磁盘系统的 性能: (1) 寻道时间 ( seektime ), 读/写磁头从一 个磁道移到另一个磁道所需要的时间; (2) 旋转延迟 (rotation delay ) 或等待时间 (latency time ) , 盘片旋转一周所需要时间的一半,也就是读/写磁头到达所要求磁道后,等待盘片旋转使读/写磁 头位于所要存取的数据(扇区)上所需要的平均时间; (3) 存取时间 (access time )., 即寻道时 间和等待时间 之和; (4) 传输速率 ( transferrate ), 在磁盘上读出或写入数据的速率。需要注意 的是,在区位记录存储情况下,盘片旋转一次边缘道通过读/写磁头传递的数据要多于内区道, 因此数据传输速率依所使用盘片部分的不同而有所变化。 限制磁盘存取时间和传输速率的一个因素是磁盘系统旋转的速度。为了支持高速旋转,这 些系统里的读/写磁头不接触盘片,而只是“悬浮”在盘片表面。磁头与盘片之间的空间很小, 以至于一粒小小的灰尘都可能卡在其中,并因此损坏磁盘和磁头,这一现象便是磁头划伤 (head crash )。 因此,磁盘系统出厂时都密封在箱子里。凭借这样的构造,磁盘系统能够以每秒几千次 的速度旋转,达到每秒数以 MB 的传输速率。 因为磁盘系统的操作需要物理运动,所以难以与电子电路的速度相比。电子电路延迟时间 是以纳秒(十亿分之一秒)甚至更小计算的,而磁盘系统的寻道时间、等待时间和存取时间是 以毫秒(千分之一秒)度量的。因此,与电子电路等待结果的时间相比,从磁盘系统检索信息 所需要的时间非常长。 磁盘存储系统不是唯一应用磁技术的海量存储设备。一种更古老的形式是磁带 (magnetic 22 第 1 章数据存储 tape ) (见图1-10),在这些系统里,信息存储在一条细薄的塑料带的磁涂层上,而塑料带则绕在 磁带卷轴上作为存储器。为了存取数据,磁带应装到称为磁带驱动器的设备里,并可以在计算 机控制下读带、写带和倒带。磁带驱动器有大有小,小至盒式机,大至比较老式的大型盘式机。 盒式机又称为流式磁带机,磁带的外表与立体声收音机类似。虽然这些磁带机的存储容量依赖 于所使用的格式,但是大多数都能达到几 GB 。 带盘 卷盘 磁带的一个主要缺点是,在磁带卷轴之间要移动的带子很长时,在一条磁带不同位置之间 移动非常耗费时间。于是相对于磁盘系统而言,磁带系统的存取时间比较长,因为磁盘的读/ 写磁头只需要做短的移动就可以在不同的扇区存取数据。因此,磁带机对于联机的数据存储不是 很常用。但是,磁带技术常应用在脱机档案数据存储中,原因是它具有容量大、可靠性高和性价 比好等优势,但其他技术(如 DVD 、 闪存等)的进步,正迅速地吞噬磁带系统最后的阵地。 1-3.2 光学系统 另一类海量存储器所应用的是光学技术 , CD ( CompactDisk , 光盘)就是其中的一种。光盘 的直径为12 cm (大约5英寸),由涂着光洁保护层的反射材料制成。通过在反射层上创建偏差的 方法在光盘上面记录信息。激光束通过监视 CD 快速旋转时反射层的不规则反射偏差来读取信息。 CD 技术最初用于音频录制,使用称为 CD-DA (Compact Disk-Digital Audio , 数字音频光盘) 的记录格式,而今天 CD 作为计算机的数据存储设备,实质上使用的仍是同样的格式。特别值得 一提的是, CD 上的信息存储在一条磁道上,它呈螺旋形缠绕在 CD 上,很像老式唱片里的凹槽, 不过与老式唱片不同的是, CD 上的磁道是由内至外的(见图1-11)。这条磁道被划分为称为扇 区的单元,每个扇区都有自己的标识,数据存储容量为2 KB , 相当于在音频录制时的音乐。 数据记录在分为若干扇! K 的磁 道上,磁道向外螺旋形旋转 光盘运动方向 图 1-11 CD 存储格式 1.3 海量存储器 23 需要注意的是,盘片外部边缘的螺旋磁道距离比内部磁道距离要长。为了使 CD 的存储能力 达到最大,信息就按照统一的线性密度存储在整个螺旋形磁道上。这就意味着,螺旋形磁道上 靠外边缘的环道存放的信息比内部的环道多。所以,如果盘片旋转一整圈,激光束在扫描螺旋 形磁道外面的部分时读到的扇区个数要比里面的部分多。因此,为了获得统一的数据传输速率, 根据激光束在盘片上的位置, CD - DA 播放器能够调整盘片的旋转速度。但是用于计算机数据存 储的大多数 CD 系统,盘片旋转的速度是比较迅速和恒定的,因此其 CD 驱动器必须适应数据传输 速率的变化。 由于采用这种设计思想, CD 存储系统在处理长且连续的数据串(如音乐复制等)时表现最 好。但是,当一个应用需要随机存取数据项时,磁盘存储器所用的方法(单个、同心磁道被划 分成立存取扇区的形式)就优于 CD 所用的螺旋形方法。 传统 CD 的存储容量是600〜700 MB 。 但是, DVD (Digital Versatile Disk )® 可具有多达几 个 GB 的存储容量,它由多个半透明的层面构成,精确聚焦的激光可以识别其不同的层面。这种 盘片能够存储冗长的多媒体信息,包括完整的一部电影。最后,蓝光技术 ( Blu - raytechnology ) 使用蓝色(而非红色)激光,能够极为精确地聚焦激光束。因此, BD ( Blu-ray Disk , 蓝光 光碟)的容量是 DVD 的5倍多。为了满足高清视频的需要,我们需要使用这种容量很大的存储 设备。 1.3.3 闪存驱动器 基于磁学和光学技术的海量存储系统的一个普遍特征是,通过物理运动来存储和读取信息, 例如,旋转磁盘、移动读/写磁头和扫描激光束等。这就意味着,数据存储和读取的速度比电子 电路的速度要慢。 闪存 (flash memory ) 技术有潜力克服这个缺点。在一个闪存系统里,用电子 信号将二进制位直接送到存储介质中,电子信号使得该介质中二氧化硅的微小晶格截获电子, 从而转换微电子电路的性质。因为这些微小晶格能够保持截获的电子很多年,所以闪存技术适 合存储脱机数据。 尽管存储在闪存系统里的数据能够像在 RAM 应用中一样,以小字节单元存取,但是现代技 术规定存储的数据应批量檫除。不过反复的檫除会逐渐损坏二氧化硅的晶格,这就意味着现今 的闪存技术不适合主存储器应用,主存储器的内容在一秒钟可能被改变许多次。然而,在某些 应用里改变可以被控制在一个合理的水平,例如数码相机、移动电话、手持式 PDA , 所以闪存 已经成为海量存储技术的一个选择。的确,因为闪存对物理震动不敏感(与磁学系统和光学系 统不同),它在便携式应用中的潜力巨大。 闪存设备称 为闪存驱动器 (flash drive ), 容量可达到几 GB , 可用于一般的海量存储应用。 闪存设备被封装在小的塑料格子里(长约3英寸),其一端有一个可以取下的帽,当驱动器处于 脱机状态时,可以保护这个设备的电子连接器。这些便携设备容量大,很容易连接到计算机以 及从计算机断幵,对于脱机状态的数据存储是很理想的选择。不过,由于它们的微小存储晶格 的缺点,当涉及真正长期应用时,它们不如光学盘片可靠。 闪存技术的另一应用是 SD 存储卡 (Secure Digital memory card ), 简称 SD 卡。 SD 卡的容量 高达2 GB , 它们被制成塑料封装的晶圆,有邮票大小(事实上还有更小的小型和微型 SD 卡), SDHC 存储卡 (Secure Digital High Capacity memory card , 高容量 SD 存储卡)的存储容量可以高 达32 GB , 而作为新一代 SD 卡的 SDXC 存储卡 (Secure Digital Extended Capacity memory card ), 其容量可超过 1 TB 。 凭借不占空间的体积,这些卡可以方便地插入小型电子设备的插槽。因此, ① DVD 全称也作 Digital Video Disc 。 ——编者注 24 第 1 章数据存储 它们是数码相机、智能手机、音乐播放器、汽车导航系统,以及其他许多电子应用的理想选择。 1.3.4 文件存储及检索 海量存储系统中的信息一般被分组为较大的单元,即文件 ( Ale )。 典型的文件可能由文本 文档、照片、程序、音乐录音或者一组有关公司员工的数据组成。我们已经了解到,海量存储 设备规定这些文件要以较小的多字节单元进行存储和检索。例如,存储在磁盘上的文件必须按 照扇区操作,每个扇区都有固定的规格。符合存储设备特性的数据块称为物理记录 (physical record ) 0 因此,海量存储系统中的大文件通常包含多个物理记录。 与这种物理记录划分相对,文件通常还有其自然划分,这由它所表示的信息决定。例如, 一个包含公司员工信息的文件由许多单元组成,其中每个单元包含一个员工的 信息; 一 个有关 文本的文件包含段落或页。这些自然产生的数据块称为逻辑记录 ( logicalrecord )。 逻辑记录通常由称为字段 ( field ) 的较小单元组成。例如, 一 个包含员工信息的逻辑记录 大致由姓名、地址、员工标识号等字段组成。有时候,文件的每一个逻辑记录是由一个特定的 字段唯一标识出来的(也许是一个员工的标识号、一个部门标号或者是目录项标号)。这样的标 识字段称为键字段 ( keyfield ), 键字段中的值称为键 ( key )。 逻辑记录的规格很少能与海量存储系统的物理记录相匹配。因此,人们可能会发现若干逻 辑记录存放在一个物理记录里,或者一个逻辑记录存放在两个或者更多的物理记录里(见图 1-12)。因此,海量存储系统的信息检索需要一定的整理工作。这个问题的一个常用解决方法是, 在主存储器里留出一个足够大的区域,用于存放若干物理记录并将此存储空间作为重组区域。 也就是说,与物理记录兼容的数据块可以在主存储区与海量存储系统之间传输,主存储区的数 据能够根据逻辑记录引用。 逻辑记录对应于数据内的自然划分 物理记录对应于扇区的大小 图 1-12 磁盘上的逻辑记录与物理记录 这种存储区域称为缓冲区 ( buffer ). 一般情况下,缓冲区是在一个设备向另一个设备传输 数据的过程中临时存储数据的区域。例如,现代的打印机都有自己的存储电路,其大部分被用 作缓冲区,用于保存该打印机已经收到但还没有打印的那部分文档。 问舉 身练习:: .果: 瓣巧 :義_|§___囊__ i :: : ,“,':::晨 醒麵_麵_廳圓腳^||^_|^|||_議鷄__; 1.4 用位模式表示信息 25 3,为什么在一令爾订系 攀里、 靡.麟需學经常萬新的蜱釋琴 齊鱗奔 释载旱厶習荀 裹卽^ : ; ; :岑_学__序修有时_— : 段法:本木__^_獅_ - —个符夸的增加就_#文 件増加 几百个字节。为什么? % : / : J . .:: : : ■:* *• .*.( - ;h k . ;" * * J r ' ■- - i - : • P - * *; . . -. -_ I 〆 ...-*--- J , .1 - 1. ••• _- .-.J 七 ..• . ... •一- .---. - r . - - *- . * I - .*••.•*: ^ • : • • ••_•. . -... 二 . . --?•• . •.... -6 ■什么桌_冲囟? ’ „- '- 1.4 用检摄式表示信息 在研究了位存储的技术后,现在来了解如何将信息编码为位模式。我们的学习集中在对文 本、数字数据、图像以及声音等编码的流行方法上,其中每一个编码系统都可能会影响到典型 的计算机用户。我们的目标是充分了解这些技术,以便知道应用这些技术的效果。 1.4.1 文本的表示 文本形式的信息通常由一种代码表示,其中文本中的每一个不同的符号(例如字母和标点 符号等)均被赋予唯一的位模式。这样,文本就表示为一个长的位串,连续的位模式逐一表示 原文本中的符号。 在20世纪的40年代〜50年代,人们设计了许多这样的代码,并结合不同的设备使用,随之 增加了不少通信问题。为了缓解这种情况, ANSI (American National Standards Institute ,美国 国家标准化学会)采用了 ASCII (American Standard Code for Information Interchange , 美国信息 交换标准码)。这种代码使用长度为7的位模式来表示大小写英文字母、标点符号、数字0〜9以 及某些控制字符,如换行、回车与制表符等。今天, ASCII 码被扩展为8位位模式,方法就是在 每个7位位模式的最高端添加一个0。这个技术不仅使所产生的代码的位模式与字节型存储单元 相匹配,而且还提供了附加的128个位模式(通过给附加的位赋予数值1),可以表示除英语字母 和关联的标点符号之外的符号。 :美国国 家标牵 化学备 ( ANSI ) .成立于 19 t 8 年,学备 T 屬•政 侍洩 ,鼻 剥雖拜 a 綱泰賴•员 V : 商业 is 孽相也 魯姜 以或政養我展。它代表美国作为 iso 的奔员,零的釋摔是卿取 : :| | : I ? 其他国家:▲似且织 包華 鱗大 ㈤ 亚标秦组织、加拿禾标准套员会' 中貝 __碌黨_术监 督局、 梅嵙本工 ㈣ 准秦異舞,, ; 轟瑪哥标旨导義 举邊董委負会:瑞士标准化协会‘矣国标遶学备。 ’ - :二 v 国靡标准化組鋼泌常称為 ISO 1 )、 建立 f l 々47 年,是世界范围#_獒体敢碰;:遮也參 分趔来 自各个国家。今,它的总部设在^士日内瓦,有100多个实体会员和许_秦_ 7 (规察会员也是^些国 家的 标准化实体,::连些国索还没有全国鍊;一尚标 灰把卖 能直接参与标准的开发,拉苛以了解 ISO 的活着 。 ) ISO 的网站是 http://wwwliso;org; 26 第 1 章数据存储 8位模式的一部分 ASCII 码可见附录 A 。 利用这个附录,■我们可以将位模式 01001000 01100101 01101100 01101100 01101111 00101110 解码为报文 “ Hello .”, 见图1-13。 : 卩; c . r 、... .:^:七 :-:-.-.:.-。.. ;... ,\\' \ r -- y . zT .-::-" - .•'■■■' : ■- - ■. ■: . .. . •.二 _ 一一 . ■---- v ■■ 、 j : . 一二―二 - ■ ' 、乂 : - ■■■ — ■:■* ■■“ r ■- v ^ ■ _ 、; ■ _ ■ ■ ■■ d ,■ ” 」 r ._- ■ I Q ^ 图 1-13 报文 “Hello.” 的 ASCII 码 ISO (International Organization for Standardization , 国际标准化组织,这个组织的缩写也暗 指了希腊语中意味“平等”的单词 “ isos ”) 开发了大量 ASCII 扩展,每种扩展都是针对某一主 要语种设计的。例如,其中一个标准提供了表达大部分西欧语言文本所需的符号。在其128个附 加模式中有表示英磅和德语7^音 a 、 6、 ( i 的符号。 ISO 扩展的 ASCII 标准在支持全世界多语通信方面取得了巨大进展,但是仍有两个主要障 碍。首先,扩展的 ASCII 中额外可用的位模式数不足以容纳许多亚洲语言和一些东欧语言的字 母表。其次,因为一个特定文档只能在一个选定的标准中使用符号,所以无法支持包含不同语 种的语言文本的文档。实践证明,这两者都会严重妨碍其国际化使用。为弥补这一不足 , Unicode 在一些主要软硬件厂商的合作下诞生了,并迅速赢得了计算机行业的支持。这种代码采用唯一 的16位模式来表示每一个符号。因此, Unicode 由65 536个不同的位模式组成足以表示用中 文、日文和希伯来文等语言书写的文本。 由一长串根据 ASCII 或 Unicode 编码的符号组成的文件常 称为文本文件 (text file )。 重要的是 要区别下面两类 文件: 一类是由称 为文本编辑器 (text editor , 常简 称为编 辑器)的实用程序操 作的简单文本文件;一类是 由字处理程序 (word processor ), 如微软的 Word 产生的较复杂的文 件。两者都是由文本材料组成的,但是,文本文件只包含文本中各个字符的编码,而由字处理 程序产生的文件还包含许多专用格式码,用于表示字体变化、对齐信息等。 1.4.2 数值的表示 当所记录的信息只有数值时,以字符编码的形式存储信息效率就会很低^为了了解其中的 原因,让我们来看看数值25的存储问题。如果我们坚持用 ASCII 编码符号来存储,每个符号一 个字节,那么总共需要16个二进制位。此外,用16个二进制位可以存储的最大数是99。不过, 我们马上就可以看到,使用 二进制记数法 (binary notation ), 16个二进制位可以存储0〜65 535 范围内的任何一个整数。因此,二进制记数法(或它的变体)被广泛应用于计算机存储器中 数值数据的编码。 二进制记数法是一种数值表示方法,只使用数字0和1,区别于传统的使用数字0、1、2、3、 4、5、6、7、8和9的十进制记数系统。我们将在 L 5 节中更详细地学习二进制记数法,现在只需 要初步了解该系统。我们来考虑一种老式的汽车里程表,它的显示轮只包含数字0和1,而不是 传统的十进制数字0〜9。里程表以全0读数开始,当汽车行驶几英里时,最右方的滚动显示轮从 0旋转至 U 当这个1旋转回0时,就使得一个1出现在它的左边,因此产生模式10;接着右边的0 旋转为1,产生11;这时,最右边的数从1旋转回0,使得它左边的1也旋转回0,这就使另一个1 出现在第3位上,产生模式100。简言之,在我们驾驶汽车时将看到下列顺序的里程表 读数: 00 00 0001 1.4 用位模式表示信息 27 0010 0011 0100 0101 0110 01X1 1000 这个序列包括了整数0〜8的二进制表示。尽管有些冗长乏味,但是我们可以扩展这种计数 技术,用以发现16个1组成的位模式是可以表示数值65 535的,这就证实了我们的说法:0〜65 535 范围内的任何整数都可以利用16个二进制位进行编码。 由于它的高效性,数字信息通常以二进制记数法的某种形式存储,而不用符号编码。我们 称其为“二进制记数法的某种形式”,这是因为上面描述的简单二进制系统只是计算机里应用的 若干数值存储技术的基础。二进制系统的某些变体将在本章的后面讨论。现在我们只需要知道, 称 为二进制补码 ( two’s complement ) 记数法(见1,6节)的系统通常用于存储整数, 因为 它提供 了一种便利地表示负数和正数的方法。为了表示4丄和$这样带有分数部分的数,我们还要使 2 4 用一种称为浮点 ( floating - point ) 记数法的方法(见 1.7 节)。 1.4.3 像的表示 通常将图像表示为一组点,每一个点称为一个像素 ( pixel , 是 picture element 的缩写),每 个像素的显示被编码,整个图像就表示成这些已编码像素的集合,这个集合被称为位图 (bit map )。 这种方法很常用,因为许多显示设备(如打印机和显示器)都是在像素的概念上进行操 作的。因此,位图格式的图像更便于显示。 在位图中的像素编码方式随着应用的不同而不同。对于黑白图像,每个像素由一个位表示, 位的值取决于相对应像素是黑还是白。大多数的传真机采用此方法。对于更加精致的黑白照片, 每个像素由一组位(通常是8个)表示,这就使得许多灰色阴影也可以表示出来。 就彩色图像而言,每个像素通过更为复杂的系统来编码。有两种方法很常用,我们称其中 一种为 RGB 编码,每个像素表示为3种颜色成分——红、绿、蓝,它们分别对应于光线的三原色。 一个字节通常用来表示每一个颜色成分的强度。因此,要表示原始图像中的一个单独像素,就 需要3个字节的存储空间。 一个较常用的可以替代简单 RGB 编码的方法采用一个“亮度”成分和两个颜色成分。这时 候,“亮度”成分(称为像素亮度)基本上就是红、绿、蓝部分的总和。(事实上,它是像素中 白光的数量,但是现在我们不需要考虑这些细节。)其他两种成分(称为蓝色度和红色度)分别 取决于在像素中所计算的像素亮度与蓝或红光数量之间的差。这3个成分合起来就包括了显示像 素所需的信息。 利用亮度和色度成分进行图像编码这种方式的普及源自彩色电视领域,因为这种方法提供 了可以同样兼容老式黑白电视接收器的彩色图像编码方式。事实上,只需要对彩色图像的亮度 成分编码就可以制造出图像的灰度形式。 位图技术的一个缺陷在于,图像不能轻易调节到任意大小。基本上,增大图像的唯一途径 就是变大像素,而这会使图像呈现颗粒状。(这就是应用于数码相机的“数字变焦”技术,与此 相对的“光学变焦”是通过调整相机镜头实现的。) 28 第 1 章数据存储 __ 为了避免缩放问题,表示图像时还可以把图像表示成几何结构的集合(如直线和曲线),这 些几何结构可以用解析几何技术来编码。这种描述允许最终显不图像的设备决定几何结构的显 示方式,而不是让设备再现特殊像素模式。这种方法被用在当今的字处理系统中,用于产生可 缩放的字体。例如 , TrueType (由微软和苹果开发)是用几何结构描述文本符号的系统,而 PostScript (由 Adobe 系统开发)提供了 一 种描述字符及更一般的图形数据的方法。这种表不图 像的几何方法也在 CAD ( Computer-Aided Design , 计算机辅助设计)系统中很常见,用于在计 算机屏幕上显示和操控三维物体的绘制。 对使用许多绘图软件(如微软的绘图工具)的用户来说,用几何结构表示图像与用位图表 示图像之间的区别是明显的,这些绘图软件支持用户绘制的图中包含预先设定的形状(如矩形、 椭圆形、基本线条等)。用户仅从菜单中选择所需的几何形状,然后使用鼠标绘制这个形状。在 绘制过程中,软件保存了所画形状的几何描述。当鼠标给出方向后,内部的几何表示就被修改, 再转化成位图形式显示出来。这种方法方便图像的缩放和形状的改变。然而, 一 旦绘制过程完 成,就会去除基本的几何描述,仅保存位图,这意味着再做其他修改需要经历冗长的一个像素 接一个像素的修改过程。另外,一些绘图系统会将描述作为几何图形保存下来并允许在之后进 行修改。有了这些系统,就可以轻松地调整图形的大小,并可按各种尺寸显示清晰图像。 1.4.4 声音的表示 为了便于计算机存储和操作,对音频信息进行编码的最常用方法是,按有规律的时间间隔 对声波的振幅采样,并记录所得到的数值序列。例如,序列0、1.5、2.0、1.5、2.0、3.0、4.0、 3.0、0可以表示这样一种声波,即它的振幅先增大,然后经短暂的减小,再回升至较高的幅度, 接着又减回至0 (见图1-14)。这种技术采用每秒8000次的采样频率,已经在远程语音电话通信 中使用了许多年。通信一端的语音被编码为数字值,表示每秒8000次的声音振幅。接着将这些 数值通过通信线路传输到接收端,用来重现声音。 编码的声波 图 1_14 序列 0 、 1.5 、 2.0 、 1.5 、 2.0 、 3.0 、 4.0 、 3,0 、 0 所表示的声波 尽管每秒8000次的采样频率似乎是很快的速率,但它还是满足不了音乐录制的高保真。为 了实现今天音乐 CDS 现声音的质量,我们需要采用每秒44 100次的采样频率。每次采样得到的 数据以16位的形式表示出来 (32 位用于立体声录制)。因此,录制成立体声的每一秒音乐需要100 多万个存储位。 乐器数字化接口(简称 MIDI ) 是另外一种编码系统。它广泛应用于电子键盘的音乐合成器, 用来制作视频游戏的声音以及网站的辅助音效。 MIDI 是在合成器上编码产生音乐的指令,而不 *1.5 二进制系统 29 是对音乐本身进行编码,因此它避免了采样技术那样的大存储容量要求。更精确地说 , MIDI 是对什么乐器演奏什么音符以及持续时间进行编码。例如,单簧管演奏 D 音符2秒钟,可以 编码为3个字节,而不必按照每秒44 100次的釆样频率用两百多万个二进制位来编码。 简言之,可以把 MIDI 看做是对演奏者乐谱编码的一种方法,而不是对演奏本身编码。因此, MIDI “录制”的音乐在不同合成器上演奏时声音可能是截然不同的。 (见附录屬 0110L001.. 1. 下面是 ASCII 编—的—条 _ 息,每个符号8位 。它 齡义是 fHM '. 0100 , 0,011 ono:im oiiioG^'a ■ oxh . oigi ,, 01100101 01 X 10&10 00100000 01010 ail 011 GG 011 oaiojoioi oiionaio 01100011 onooioi ■ •- • •••■;. •• •- • : -• • 4 丧 AS,cn 释中〆本亨字母码和相应小写宇毋码之间的芣系舉什么^ ( 见附录 a ')、 … 3. 用丄 SCH 她些捲句编 码:- 1 V : a : “ St 印!” Gheryl shoiuted . = 二 : 4. 描述一种技曰常生活中能參呈规两种状态 的# 备,例如旗杆上的旗 I | R , 或者弁起或者令降;给一种状 辛赋儐1,男—种赋为0。-后达我们看一: F :, 差以这样的位来存 鵠财, 字母 b 部 ASCJJ 妈舍怎样考 lilfcilsWWiiiiiiiilPi® a . 010-1 b . 1001 c , 1011 ; , d . 0110 .... e . 10000. £.10010 - 一 ■ : a , 6 b . 13 c . 11 d . 18 e .27 f . 4 .. - 桌采用二进制编码,那么 x 能 赛寒关 的__.?: = 乂 :〆 8,崎十进制以外,另一种奉_位權赛,其,拜 模式 ( 12 表示 00 & 01100 字:.5' Jh •'. O ,:..:..^;:':. : *•:! •>■•.;!:.:• ,>•.'•!:..:, - f , K ;;. !: 4 . 1 嚷觀 龙翱嫉 海 lil; ii l!:iii i!-'! .二 '丄人, ■ e . ii m • 6, io 工, _薄編?__!11 ::; ;;:V! ^::.'::.;: ::::r' r:^;: :••••••• .:•::•.;•, -:..:成 •!;ii;: 1 .?: •:::•!:.::;-:::;!,?!: :;^:: ; i;!':!:;;!^ ; ::..! !i;:;i; 'j!:-; !:::i : : :..i :: ^-;:^: :^!;k;..v^: ••: ,!,:.;•:•,• V:::::: :::: !:••:•::: 七; …: •^:T.::n:>:::;!:.:::^:; : _賴_|:_:【 ..i . '••I i : i. /.ii . i:!i';:;■;! :i,: Ijj :':: 1 ! :';;!! rj ' l ' i '!- ! ;■:;. : i :: -1! : ; !::N : !i.'|: j i ::; : ; Rj i :::;; M,! : "' : ; i 1 ::. l: .: : ! l: ' : ' ■- :i!:! : :!i!M': :;.! :. 11 :i!. .;!;i ; hi M..._ I.V:. I.: 1 ::,.'..in *1.6 整数存储 数学家们长久以来就对数字记数系统很感兴趣,而且他们的许多想法已经被证明与数字电 路的设计是相符的。本节,我们将研究其中两种记数系统,二进制补码记数法和余码记数法。 它们都用于在计算设备中表示整数。这些系统都是基于二进制系统的,但是增加了些其他的特 性,因而与计算机设计更加匹配。尽管有这么多的优点,它们还是有缺陷的。我们的目标是了 .解这些特性以及它们是如何影响计算机用法的。 :::鮮顯細 t 米^^義藉系 .释, / 雜 ___ ,麵麵藝嘯麵 ■ 變 猶德麵遞麵誠_驅||1«^^_鏽.. .".. - ■ ■ ■_■ 一 .V - ■ ■ - - •_. : - v ^ V :: ■- •• ... : 1.6.1 二进制补码记数法 今天计算机表不整数最普遍的系统就 是二进制补码 ( two’s complement ) 记数法。这个系统 34 第 1 章数据存储 采用固定数目的二进制位来表示系统中的每一个数值。在今天的设备中,应用二进制补码系统 是很普遍的,其中每个数值用一个32位的模式表示。这种大系统方便表示很大范围的数字’但 对于教学不是很便利。因此,学习二进制补码系统的特性时,我们将把精力集中在比较小的系 统上。 图 1-21 列出了两种二进制补码系统——一种基于长度为3的位模式,另一种基于长度为4的 位模式。这种系统是这样构成的,即先规定适当长度的一组二进制0,接着用二进制计数,直到 只有一个0,其他都是1的模式形成。这些模式表示数值0, 1,2, 3 ,…。 表示负值的模式是这样获 得的,即先规定一组适当长度的二进制1,接着按照二进制反向计数,直到只有一个1,其他都 是0的模式形成。这些模式表不数值-1,-2,-3, …。 (如果你认为利用 一 进制反向计数有困难, 那么可以仅从表格底部,即只有一个1,其他都为0的模式开始,计数到全是1的模式。) (a) 使用长度为 3 的位模式 c 淑迦 '5 d ': 巧 二七 L -:- : - ■ : ; ,■,,■,,$'," , v . ^ ■■ ■ v : : :: Q . 姐二.卜^: - - ^ : r 人 v 、二 ,… ,,- ■二 ㈣ 讎「, -. 藏.'豕緣 分酶心,:) ■肤; . p _眞:::.鄉 ㈤ 穴' ,■織$觉嫌 K 热槪 v.; ■_漬.鷄種 :7:纖_攀罐纖 .☆i 姑轉 / ■ 玲:.讀釔 r 麵%敕 :㈣ f _ — 通 ㈣ : 泛 :㈣ ..一 ..u in (b) 使用长度为 4 的位模式 图 1-21 二进制补码记数法系统 注意,在二进制补码系统中位模式最左边的二进制位指明所表示数值的符号。因此,最左 边的位常称为 符号位 (signbit ) o 在二进制补码系统中,符号位为1的模式表示负值,符号位为0 的模式表示非负值。 在二进制补码系统中,绝对值相同的正负数值之间的模式很相近,从右向左读时,直到第 一个二进制1,它们都是相同的。然后,以这个1为分界线,左面的位模式互为补码。(一个模式 的补码 ( complement ) 是通过转换所有的二进制0为1,并转换所有的二进制1为0得到的模式。) 例如,图 1-21 中的4位系统,表示2和 -2 的模式都是以10结束,但是表示2的模式开始为00,而表 示-2的模式开始为11。观察到这一点,我们就可以得出在绝对值相同的、表示正负值的位模式 之间转换的算法。我们只需要从右到左复制原始的模式直到第一个1,接着在将剩余位转换为最 终位模式时,对这些剩余位取反(图1-22)。 理解了二进制补码系统的这些基本特性,也可以得出一个二进制补码表示法的解码算法。 如果要解码的模式有一个符号位0,我们仅仅需要读出这个数值,就好像这个模式是一个二进制 表示。例如,0110表示数值6,因为110是6的二进制表示。如果要解码的模式有一个符号位1, 就知道表示的数值是负的,而我们所要做的就是找到其绝对值。为了实现这个目的,我们先要 利用图 1-22 中“复制及取反”的步骤,然后对获得的模式进行解码,就仿佛它只是一个简单的 二进制表示。例如,为了对模式1010解码,首先我们意识到,因为这个符号位是1,表示的数值 *1.6 整数存储 35 就是负的。因此,我们利用“复制及取反”步骤获得了模式0110,认识到这是6的二进制表示, 然后得出 结论: 原始的模式表示-6。 图 1-22 利用二进制补码记数法用 4 个位对数值 6 编码 1. 二进制补码记数法中的加法 我们采用二进制加法中使用的算法来计算二进制补码记数法中的数值相加,只是包括答案 的所有位模式长度都相同。这就意味着,在二进制补码系统的加法中,由于最后一个进位,答 案左边产生的任何一个附加位都要删除。因此,“加法运算”0101和0010得出0111, 0111和1011 得出0010 (0111+1011=10010, 缩减为0010)。 根据这个理解,我们来分析一下图 1-23 中的3个加法问题。每一个情况,我们都把问题转化 为二进制补码记数法(釆用长度为4的位模式),演示先前描述过的加法过程,然后对结果进行 解码,回到一般的十进制记数法。 图 1-23 转换为二进制补码记数法的加法问题 注意,图 1-23 的第3个问题涉及正值和负值的加法,它展示了二进制补码记数法的一个主要 优点: 任何带符号数字组合的加法都可以利用相同的算法,于是也就可以用相同的电路。这与 人们传统的计算法是截然相反的。尽管小学生先学加法,然后是减法,但是应用二进制补码记 数法的计算机只需知道加法就可以了。 例如,减法问题 7-5 与加法问题7+ (-5) 是一样的。因此,如果人们命令计算机执行7 ( 存 储为 0111) 减5 (存储为0101),那么它首先要转换5为 -5 (表示为1011),然后执行0111+1011 的加法过程,得到代表数值2的0010,如下 所示: 7 0111 0111 ^ 0101 -> + 1011 0010 4 2 因此我们可以看到,当二进制补码记数法用于表示数字值时,一个加法电路与一个取负电 36 第 1 章数据存储 路的组合就足以解决加法以及减法的问题了。(这些电路的图示及解释详见附录 B 。 ) 2. 溢出问题 我们在前面的例子中忽略了这样一个问题,就是在任意的一个二进制补码系统中,都有对所 表示数值大小的限制。当使用4位模式二进制补码时,可以表示的最大正整数是7,最小负整数是 -8。具体来说,数值9无法被表示出来,这就意味着我们不能指望得出5+4的正确答案。事实上, 它的结果会为-7。这种现象称 为溢出 ( overflow )。 也就是说,溢出指的是这样一个问题,即计算 得出的数值超出了可以表示的数值范围。使用二进制补码记数法时,两个正值或负值分别相加都 可能会出现这种情况。无论哪种情况,检查答案的符号位就可以发现溢出的条件。如果两个正值 相加的结果是负值的模式,或者两个负值相加的结果为正,那么就发生了溢出问题。 当然,使用二进制补码系统的大多数计算机的位模式都比例子中的长,因而在进行较大数 值操作时不会产生溢出。今天,人们普遍使用二进制补码记数法的32位模式来存储数值,可以 得到的最大正值是2 147 483 647。如果需要更大的数值,我们可以使用更长的位模式,或者改 变度量单位。例如,若在解答一个问题时用英尺代替英寸,所得数值就变小了,而且也可以达 到所要求的精确度。 关键问题是计算机会制造错误。因此,使用计算机的人一定要意识到可能涉及的危险。其 中一个问题就是,计算机程序员和使用者会自满而导致忽视一个事实,即小数值可以累加成大 数值。例如,人们过去普遍使用二进制补码记数法的16位模式表示数值,这就意味着出现大于 或等于2 15 =32 768的数值时就会产生溢出。1989年9月19日,一家医院多年来运行良好的计算机 出现了故障。仔细检查后发现,那天距1900年1月1日共32 768天,而计算机的程序正是基于那个 起始日期开始算日期的。因此,由于溢出原因,1989年9月19日 的日期产生了负值——设计计算机程序时没有考虑到这种现象。 1.6.2 余码记数法 表示整数值的另外一种方法是余码 记数法 ( excessnotation )。 与二进制补码记数法相同,余码记数法中的每一个数值都表示为 相同长度的位模式。为了建立一个余码系统,我们首先选择所使 用的模式的长度,然后根据二进制记数呈现的顺序写下那个长度 的所有位模式。接着我们发现,二进制1作为其最髙位的第一个模 式大约就在数列的中间。我们用这个模式表示0,其前的模式就分 别用于表不-2,-3,…,其后的模式分别用于表示1, 2, 3,…使 用长度为4的模式产生的编码见图1-24。我们可以看到,模式1101 表示数值5, 0011表示数值-5。(注意,余码系统和二进制补码系 统的区别就是符号位相反。) 顯_證!!麵_| 纖霉編 fa 荽灣纖 窮肇 _ ■編繼:舉 顧数 V 麵麵醒__ : :二壤|籍*編 喊零祕 ■ SS 攀 :簾, f 感顏 图 1-24 余8代码转换表 图 1-24 表示的系统称为余8记数法。为了了解其由来,首先我们 用传统二进制系统的编码翻译每一个模式,然后将其与余码记数法 表示的数值进行比较。对于每一个模式,你会发现二进制解释值比 余码记数法解释值都要大8。例如,模式1100用二进制记数法表示 为数值12,在余码系统中则表示4; 0000用二进制记数法表示为数 值0,但是在余码系统中则表示为-8。与此类似,在基于长度为5 的位模式的余码系统中,模式10000用于表示0而不是通常的数值 16,该记数法称为余16记数法。同样,你可以证明3位余码系统应 该称为余4记数法(图1-25)。 : 圓戀 翊参'纖■纖 :鐵嘛_攀鑛_獅 ::獅痛驗獅儒参 图 1-25 使用长度为3的位模 式的余码记数系统 问麵 _习 1. 将下面每一个二进制补码表示转换为相应的十 进制® 式。 a.' ; 0GO : 'll ,:: B, : 01111 :: ■ c.lll'00 ■ d 11.010;; ' e. OOOOO' £-10MG ::, ' 2. 用 8 位位模式将下列每一个十进制表示转换为相应的二进制补码形式。 也6 b,-6 d. -17 d : \P - 0. -1 f 0 3. 假定下列位模式表示的是用二进制补码记数法存储的数值,求出每一个值的负值的二进制补码表示。 a, 00:000001 b; : C.'llllllOO: d. 11111110 e. 00000000 f. 011.11111 处煆定一台机器裙玄 进制徘 码稱義鐘參储錐如_机器分别釆用下到长度 ii 傯糢式,那么可以存储的 最大数和最小数分别是什么? a. 4 b. 6 c. 8 5. 在下列问题中,每个位模式表示一个用二迸制补码存储的数值。请执行文中所述的加法过程,按照二 :进制补码谒数法求坶 審働的 笞案。并将歸题巍答案转换为十进制记数法进行验证。 a. 0101 b. 0011 c. 0101 d. 1110 e. 1010 .:丰 0^0 IS .: + :-,0Q::Ql. .+、1.01:0 ■ ■+ 0:01,1 ■ +11X0 6. 计算下列由二进制补码记数法表示的问题,但这次要观察溢出问题,并指出嘟个 答案因 产生溢出而不 .IE 确? ... a. 0100 b. 0101 c. 1010 d 1010 e. 0111 O^ll' ■*. : '01I:0 :■ + 10IQ ::. 十 ; + 00 : G:1 ' .」—一 •.. 7. 将下列问题从十进制记数法转换为长度为 4 的位模式的二进制补码记数法,然后将每一个问题转换成 ; f 个相座 的加法问题(如 计算細 的做減) ,:屬 后执行加法。将求得插答案转换为十雄制圮数法以进行 -证。 这_ 6 b . 3 c. 4 d. 2 e. 1 — (― 1) — 2 一 6 (一 4) — 5 •二进制补妈记雜祛課,。广个 Ig 數和一 个翕_ 相雜 时会产 生猶输 吗?请说明_:由 6. 9. 备下面每一个余8码表示#换为相应的十进制形式(解题时木要看文中的表格)。 a. mo .b.,0111 c. looo ,; : : , : d., ooio :: e. : ; ; om.o : . : ,, : . : 10. 将下列的每一个十进制表示转换为相应的余8码形式(解题时不要看女中的表格)。 a. 5 b. -5 q . 3 d. 0 e. 7 f. -8 11 . 数值 9 可以用余 8 铯數法 表_?用余 4 记数袪表示 6 昵?请谠明理由。 *1-7 小数的存储 不同于整数存储,对于包括小数部分的数值,我们不仅要存储代表其二进制表示的0和1, 还有其小数点的位置。一种流行的方法是基于科学记数法的,称 为浮点 ( floating - point ) 记数法。 1.7.1 浮点记数法 让我们以只用一个字节存储的例子来解释浮点记数法。尽管计算机通常使用更长的模式, 这种8位格式也可以表示实际的系统,既可以表示重要的概念,又避免了长字节的混乱。 首先我们要规定这个字节的高位端为符号位。再次说明,符号位中的二进制0代表存储的数 值为非负,1代表数值 为负。 接着,我们将这个字节剩余的7个位分为2组,或称其 为域: 指数域 (exponent field ) 和 尾数域 (mantissa field )。 我们规定符号位右边的3个位为指数域,余下的4个 38 第 1 章数据存储 位为尾数域。图 1-26 描述了如何拆分字节。 我们可以借助下面的例子解释这些域的含义。假如一 个字节由位模式01101011组成。利用前面的形式分析这个 模式,可以看出符号位是0,指数是110,尾数是1011。为 了对这个字节解码,我们首先要求解它的尾数,并在它的 左边放置一个小数点,于是得到 .1011 ^ 接着,我们求解指数域 (110) 的内容,并将其解释为一个用3位余码方法(见图 1-25) 存 储的整数。因此,我们所举例子的指数域模式表示正数2。这就要求我们将上面所得结果的小数 点向右移动2位。(负指数域就意味着向左移动小数点。)因此,我们可以得到 10.11 这就是2的二进制表示。接着,我们看到例子中的符号位是0,因此表示的数值是非负。可以 4 得出 结论: 字节01101011表示2^。如果模式是11101011 (除了符号位都与之前相同),表示的 4 数值就将为_2上。 4 再看一个例子,字节001111⑻。求尾数后得到 .1100 然后将小数点向左移动一位。指数域 ( oil ) 表示数值-1,因此得到 .01100 这表示3/8。因为原始模式中的符号位是0,所以存储的数值是非负。我们得岀 结论: 模式00111100 表示3/8。 -位的位置 尾数 指数 符号位 图 1 - 26 浮点记数法成分 用浮点记数法存储数值,我们要颠倒前面的过程。例如,为了对编码,我们首先要将其 O 用二进制记数法表示,得到1.001。接着,我们要从左到右将其位模式复制到尾数域,要从二进 制表示的最左边的1开始。此时,这个字节 如下: __ 1 0 0 1 我们现在必须填充指数域 D 为了达到这个目的,假定尾数域的左边有一个小数点,然后规 定位的数量以及小数点移动的方向,以此得到原始的二进制数字。我们在例子中可以看到, .1001 中的小数点要向右移动一位才能得到1.001,指数因此为正,所以我们将101 (在余4记数法中表 示为正1,见图 1-25) 置于指数域。最后,因为存储的数值是非负的,我们用0填充符号位。完 成的字节 如下: 0101100.1 当填充尾数域时,你可能会漏掉一个微妙的细节,这个规则是从左至右复制以二进制表示 的位模式,并要从最左边的1开始。为阐述清楚,让我们考虑一下存储数值 I 的过程,它用二进 制记数法表示为.011。这时,其尾数为 *1.7 小数的存储 39 而不会是 _ 0 110 这是因为我们是从二进制表示最左边的1开始填充尾数域。遵循这个规则的表示称为规范 化形式 (normalized form ) 。 使用规范化形式减少了同一数值多种表示的可能性。例如,00111100和01000110都可以解 码成 i , 但是只有第一个模式才是规范化形式。遵循规范化形式也意味着,所有非0数值的表示 都会有一个以1开始的尾数。不过,数值0是一个特例,它的浮点表示就是全部为0的位模式。 1.7.2 截断误差 如果要利用1字节浮点记数法存储数值那么让我们考虑因此会出现的恼人问题。我们 8 首先用二进制写得到 10.10 U 但是,当把这个模式复制到尾数域时,我们就用尽了空间, O 最右边的1 (表示最后的 1) 因此丢失了(图1-27)。如果现在忽视这个问题,继续填充指数域 O 和符号位,那么我们最后得到的位模式将为01101010,它表示的是21,而不是这个现象 2 8 称为截 断误差 (truncation error ) 或舍 入误差 ( round-off error )。 这就意味着,由于尾数域空间 不够大,存储的部分数值丢失了。 原始表不 X 0 V i 01二进制表示 I ■1 '0. 原始位模式 1 :: % r i 0 1 0 r ~ ~ r~~ ~ ■ I ―― 丟失的位 尾数 指数 符号位 图 1-27 数值2#的编码过程 8 使用较长的尾数域可以减少这种误差的发生。事实上,今天生产的大多数计算机都至少采 用32位存储浮点记数法表示的数值,而不是我们在本书中采用的8位。这同时使得指数域也更长。 不过,即使有这样较长的格式,有时候还是需要更高的精确度。 截断误差的另外一个来源就是在十进制记数法中比较常见的一个现象,即无穷展开式问题, 例如发生在我们用十进制形式表示1/3的时候。无论我们用多少位数字,有一些数值都不能被精 确地表示出来。传统的十进制记数法与二进制记数法区别在于,二进制记数法中有无穷展开式 的数值多于十进制。例如,数值1/10表示为二进制时为无穷展开式。想象一下,一个粗心的人 用浮点记数法存储和处理美元与美分时会产生什么样的问题?尤其是,如果美元被用作度量单 40 第 1 章数据存储 位,那么一角就不能被精确地存储。其中一个解决方式就是,以分为单位处理数据,这样所有 的数值就都是整数,都可以用诸如二进制补码这样的方法精确存储。 .. .. . _ 1.7 节介绍的浮点表示法过于简单,不能用于实际的计算机中。毕竟,在全部实际数字中, 这一表示法的8位只能表示其中256个数。我们在讨论中使用了8位模式来保持示例的简单性, 但依然涵盖了重要的基本概念' 用:8位表示指数(键用超碑表示法),用23位表示尾数。因此,竽:精灰浮点袭多肴7位:十邊制肴 效数字,可以表示极大的数(数量级为10 38 )直至极小的数(数量级为10$)。也就是说,给 定一个十进制数,可以非常精确地存储7位十进制有效數字,但仍 有可 能存-在少量 i 襄攀)。前7 位之后的数字一定会因截断误差去失,但歎字的近似隹会被保留下来。另^种形式赢64位的 砵精度浮4数,最多有15 位有敢 數字。 截断误差和与之相关的问题是工作在数值分析领域的人们每天都很关注的问题。这个数学 分支研究的是执行大规模、高精度有效计算所涉及的问题。 下面的例子可以激起任何一位数值分析家的兴趣。假设我们要应用前面定义的1字节浮点记 数法来做这3个数值的 加法: ^111 2 —+_+— 2 8 8 如果我们按照上述顺序加数值,首先就是加上得到2^,二进制表示为10.101。遗憾的 是,因为这个数值不能被精确地存储(如同前面所看到的),我们第一步的结果最后被存储为21 2 (与其中一个加数相同)。下一步是把这个结果再加到最后的1上。截断误差在这里再一次出现 8 了,最后的结果是错误的2丄。 2 现在让我们以相反的顺序来加这些 数值: 首先将 i 加到得到丄,其二进制表示为.01。 8 8 4 因此,第一步的结果在一个字节里被存储为00111000,这是精确的。然后,我们将这个丄加到 4 数列中的下一个数值2丄,得到2$,我们可以将其在一个字节里精确地存储为01101011。这次 2 4 的答案是正确的。 总而言之,在浮点记数法表示的数字值加法中,它们相加的顺序很重要。问题是,如果一 个大数字加上一个小数字,那么小数字就可能被截断。因此,多个数值相加的一般规则是先相 加小数字,这是为了将它们累计成一个大数字(通过加到更大的数值上)。这就是前面例子中反 映的现象。 今天商用软件包的设计师们在这方面做得很好,他们使没有经过培训的使用者也能很好地 避免这种问题的发生。在一个典型的电子制表软件系统中,除非相加的各个数值大小差别达到 10 ]6 或更多,否则所得结果都是正确的。因此,如果你认为有必要对数值 10 , 000 , 000 , 000 , 000,000 加1,那么你会得到 答案: *1.8 数据压缩 41 10 , 000 , 000 , 000 , 000,000 而不是: 10 , 000 , 000 , 000 , 000,001 这样的问题在一些应用中是很严重的(例如导航系统),小误差可能在加法运算中累加,最终产 生严重的后果。但是,对于一般的 PC 使用者,大多数商用软件提供的精确度己经足够了。 ::.箱攤 ' ...... ... : : . 1. 用文中所述的浮点格式对下列泣模式进行解妈。 a . 01001010 01.101.101 ; c . QQ 111 G 01 d . 11 Olid 00 e . 10101011 2 . 将下列數憧编麵女举 ㈣ 速的 齅点# a , 截断读差的出现情磁。「 a _ 2 ! b . 4 d . — 3 — 4, 2 ——8 釓:粮擴 Sc 中所 述衝浮点格式,禱武和:祕111101申梛一个表示的懂更夭?售逨工神摘陡#个模 式表示的值更大的简单过程。 4. 使用文中所述的樣点格式时,两以表示韵最大值是什么?可以表示的最 小正値 是什么:? I _ . 1 1 •: : i : : • • '■ • • ..... 费 m ‘ L ' - 7. 说出把数字信息、图像和声音编码为位模式时一种常见的麻烦规拿。 *1_9 通信差错 __ 当信息在计算机的各部分之间来回传输,或在月球和地球之间来回传输,又或者只是被保 存在存储器中,最终检索到的位模式有可能和最原始的不一致。灰尘粒、磁盘表面的油脂或者 一 个出了故障的电路都可能使数据被错误地记录或读取。传输过程的静电干扰可能会损坏一部 分数据。另外,在某些技术条件下普通的隐蔽放射线可以改变计算机主存储器中的存储模式。 为了解决这样的问题,人们幵发了许多编码技术来检测甚至校正错误。今天,由于这些技术被 大规模地内置于计算机系统的内部构件,计算机使用者并不了解它们。不过,它们是很重要的,为 科学研究作出了很大的贡献。因此,我们有必要了解一些使计算机设备可靠的技术。 1.9.1 奇偶校验位 一 种简单的错误检测方法基于下面的原则,即如果被操作的每个位模式都有奇数个1,但却 找到了有偶数个1的模式,那么一定是出错了。使用这个原则,我们需要这样一个编码系统,其 中每个模式有奇数个1。这是很容易做到的,首先在编码系统已经可用的模式(也许在高位端) 上添加一位,称 为奇偶校验位 ( paritybit )。 在任何情况下,我们给这个新的位赋值1或0,这样 整个模式就有奇数个1。一旦我们这样调整了编码系统,有偶数个1的模式就表示出现了错误, 被操作的模式也是不正确的。 图 1-28 向我们展示了如何将奇偶校验位加到字母 A 和 F 的 ASCII 码上。注意, A 的编码变成了 101000001 (奇偶校验位为1), F 的 ASCII 码变成了001000110 (奇偶校验位为0)。尽管 A 原始的8 位模式有偶数个1, F 原始的8位模式有奇数个1,但它们的9位模式都有奇数个1。如果将这种技 术应用于所有的8位 ASCII 模式,我们就能够得到一个9位编码系统,其中任何一个9位模式有偶 数个1就表明出错了。 奇偶校验位 奇偶校验位 整个位模式含有奇数个1 整个位模式含有奇数个1 图 1-28 适用奇校验的字母 A 和 F 的 ASCII 码 上面描述的奇偶校验系统 称为奇校验, 因 为我们 设计的系统使得每一个正确的模式都有奇 数个1。另一种技术 称为偶校验。 在一个偶校验系统中,每个模式都被设计成包含偶数个1,因 此如果出现了奇数个1,那么就表明有错误。 今天,在计算机主存储器中使用奇偶校验位已经不是一件稀奇的事了。尽管我们假设这些 计算机存储单元是8位的,但事实上,它们可能是9位,其中一个位被用作奇偶校验位。每次传 输一个8位模式给存储电路存储,电路都会给其加上一个校验位,存储结果为9位模式。在后来 符号 代码: D M :po:go : op- ____華議 ^ pi - ooia - ^oiirad- -- lOOl-lGK : 101001 . 11,0103; : 图 1-29 —个纠错码 图 1-30 用图 1-29 的编码对模式010100解妈 46 第 1 章数据 存储, _ 检索模式时,电路检验这个9位模式的奇偶性。如果这样没有发现错误,存储器就移走校验位, 然后自信地返回余下的8位模式。否则,存储器返回那8个数据位,并警告返回的模式可能与原 本传给存储器的模式不同。 直接使用校验位很简单,但是有其局限性。如果一个模式最初有奇数个1,并出现了2次错 误,那么它就仍然有奇数个1,这样校验系统就无法发现其错误。事实上,直接使用校验位不能 发现模式中任何偶数个错误。 有时对长位模式应用一种方法来减少这类问题,例如磁盘扇区存储的位串。这种情况下, 模式都有一组校验位构成的校验字节 ( checkbyte ) 0 校验字节中的每一个位是一个校验位*与散 布于整个模式中的一组特殊位相联系。例如,一个校验位可能与该模式中从第一个位起的每个 第8位相关联,而另一个与该模^^中从第二位起的每个第8位相关联。这样,集中在原模式某个 区域中的一组差错就很可能被发现,因为它会在一些校验位的范围内。这个校验字节概念的演变 引出了称为校验和 ( checksum ) 及 CRC (Cyclic Redundancy Check , 循环冗余校验)的差错检测 方案。 1.9.2 纠错编码 虽然使用校验位可以发现差错,但是它不能提供纠正那个差错所需的信息。难怪设计出既 能够发现差错又能纠正差错的 纠错码 ( error-correcting code ) 会令很多人感到惊讶。毕竟直觉告 诉我们,如果不知道信息的内容就无法纠正接收信息中的错误,但图 1-29 给我们展示了一个具 有这样纠错特性的编码。 为了明白这个编码是如何运作的,我们先定义汉 明距离 (Hamming distance ,根据 R . W . Hamming 的姓氏命名,由于在20世纪40年代继电器可靠性方面备受挫折,他开始开创性地研究 起纠错码来)。两个位模式之间的汉明距离指的是这两个模式中不相同位的个数。例如,图 1-29 编码中表示 A 和 B 模式的汉明距离是4,而 B 和 C 间的汉明模式是3。图 1-29 编码中最重要的特征是, 任何两个模式之间的汉明距离至少是3。 如果用图 1-29 的模式修改单个位,就会发现错误,因为它的结果不会是一个合法的模式 。(我 们至少要将每个模式改变3个位,这样它们才会像另外一个合法模式。)而且,我们能够指出原 始模式是什么。毕竟,修改过的模式和其原始形式的汉明距离是1,而和其他任何合法模式的汉 明距离至少是2。 因此,对最初用图 1-29 编码的信息解码,我们只需要对比接收模式和用此编码表示的模式, 直到我们找到一个和接收模式之间的汉明距离是1的模式为止。我们将其视为正确的符号进行 解码。例如,我们接收到位模式010100,然后将其与用编码表示的模式相比,我们就会获得 图 1-30 中所示的表格。因此,我们得出结论,传输的字符一定是 D , 因为这是最接近的匹配。 小离 最距 与-离 式距, 的,间 到±: 收砰 接:编 S 麗»一:!::齡1 一一霊 (eMl 一 K ' l : 售 式 模 的 I 接 O b o o o o Q ; o:p tD o 5纖嫌嫌一 1 * 1 o 码: 0 OVZ---H O-Gl'lo^— oi 1 0 2 0 oi, ora''-Q,l.l o 1 p— JBmlsj — :;;^'^: . Q Q 1:1 oo--l;i 0 字 c de - fg-h 复习题 47 可以发现,使用图 1-29 的编码技术,每个模式我们可最多检测出2个错误并改正1个。如果 我们能设计出这样一种编码,每个模式和其他任何模式之间的汉明距离都至少是5,每个模式我 们就将最多发现4个错误,并最多改正2个。当然,设计一种具有长汉明距离的编码不是一件简 单的事。事实上,它是称为代数编码理论的数学分支的一部分,这个理论是线性代数和矩阵理 论的子领域。 纠错技术被广泛用于增加计算设备的可靠性。例如,它们经常被用于高容量磁盘驱动器, 以减少因磁盘表面瑕疵而损坏数据的可能性。此外,最初用于音频的 CD 格式与后来用于计算机 数据存储的格式之间的主要差别是纠错的程度。 CD - DA 格式包括的纠错特点使得错误率减少到 2张 CD 只有1个错误。这对视频录制足够了,但是对于用 CD 向客户交付软件的公司,若50%的 磁盘有瑕疵是令人无法忍受的。因此,人们将附加的纠错特征用在 CD 中来存储数据,使产生错 误的可能性减少到20 000个磁盘1个错误。 ■ 问_与細 L 卞面的字节最初是用奇校验編码的 & 找出出错的一个, :,, a. lo'oioiloi ''b!c iDO'O'O.&dpi ,c.- ; 0'(),0"0.,d^ , 0 ; '0 l 0 i " ^..lll'oooo^'o ' .'.e.'Oaiililll 1 2 •问题 1 中没发现错误的字节述会有错误吗?解释原因。、 3. 匆果将奇校验換成偶較验,那么问辱1 和傅题 2的答案又将如 何?、 - 4. 用带奇校验的 _ cn 碍对这些胥句^在每二个字符编码 的歸位 端加一个榱验位。_ b . Does 2+3=5? / ■ 5. : 用獨 1 邊的销鶴对南面的誠解篆 、 ''. a . ooixai 100100 0 Q 11 G &- : .T ..來 :: fly . : ..... :;: ;: ;.. ••:; ; •; i •: :i : :、:. 1 ;:.:i . i .: :: J ; v _ v ._ i . i _. .義.. .::.:•::::;. _ i 麵1 ': ;| : •:!• •!. ! .1.:!! .. I ::. .... ,.: _.l ..... I | :!.!:「 乂 . :. . ... g. oiioio iioiio aooaoo; .011100. 一 . 1 l 1 … -- . 乂. - . _1 : - •• ■- . ‘ :: ... 6 -用长度为 5 的位模式,为字參、 B ( C 和 D 建造编码,俥得任何商个模式之间的汉明臟至少是3。 . I ,■咕; 11 丨■■:峨明脱 賊) 〒海命:现 Ml . ' o ,.>, : >1 : ..別说 I 1 *% 如取」柳卩奶邮你挪(知赵 _ r 级-叫.奶 虹饰 _ 啦..: 你: Vl ^ :々 ..:l I ..I ..: v ; w ::久 ; 复习题 (带*的题目涉及选读小节的内容。) 1. 假设上面输入为1下面输入为0,请确定下面每 一个电路的输出值。如果上面输入为0,下面 输入为1呢? > > > ■> 2. a . 下面这个电路用来完成什么布尔运算‘ 输入> 输入> ■{>- -> 输出 b . 下面这个电路用来完成什么布尔运算 a . \ 1 J 1 E > - 输入〉 > — — 输入 > b . > — n > *3. a . 输出 路,我们会发现它有一个额外的输入端, 称为反向器 ( flip ) 。当反向器的输入从0 变成1时,输出将翻转状态(如果原来是0, 现在就是1,反之亦然)。但是,当翻转输 入从1变为0时,什么都不会发生。虽然我 们可能不知道电路完成这一行为所需的细 节,但仍然能够在其他电路中将这一设备 48 第 1 章数据存储 作为一个抽象工具来使用。请考虑使用下面 两个触发器的电路。如果在电路的输入端发 送一个脉冲,下面的那个触发器将变换状 态。但是,另一个触发器不会改变,因为其 输入(这一输入是从非门的输出接收到的) 从1变成了0。因此,这一电路现在的输出为 0和1。在电路输入端发送第二个脉冲将翻转 两个触发器的状态,并产生输出1和0。如果 在电路输入端发送第三个脉冲,将会产生什 么输出呢?发送第四个脉冲呢? 按照这个存储安排,遵循下列指令,记录下这 些存储单元的最后内容。 地址 内容 00 AB 01 53 02 D 6 03 02 步骤1:将地址为03的单元的内容移动到地址 为00的单元中。 步骤2:将数值01送到地址为02的单元。 步骤 3: 将存储在地址01中的数值移动到地址 为03的单元。 6. 如果每个单元地址用2个十六进制数字表示, 那么一台计算机的主存储器中可以有多少个 单元?如果用4个十六进制数字呢? 7. 什么位模式可以用下面的十六进制记数法表 示? a . CD b . 67 c . 9 A d . FF e . 10 b . 通常有必要协调一台计算机中不同组件的 活动。只需将脉冲信号(称为时钟)连接到 类似 a 中的电路即可。额外的门(如图所示) 将以协同的方式发送信号到其他连接的电 路。研究这一电路时,你将能够确认这样一 个事实,即在第一次、第五次、第九次(依 次类推)时钟脉冲下,输出端 A 将发送1。 哪些时钟脉冲将使输出端 B 发送1?哪些时 钟脉冲将使输出端 C 发送1?第四次时钟脉 冲时,哪个输出端为1? 输出 A 输出 C 4. 假设下面电路的两个输入都是1。请描述如果 上面输出暂时变为0,会发生什么?如果下面 输入暂时变为0,又会发生什么?用与非门重 新绘制这个电路。 5. 下面表格表示的是计算机主存储器某些单元 的地址和内容(釆用十六进制记数法)。首先 8. 下面十六进制记数法表不的位模式中,最尚有 效位的数值是什么? a . 8 F b . FF c . 6 F d . IF 9. 用十六进制记数法表示下面的位模式。 a . 101000001010 b . 110001111011 c . 000010111110 10. 假设一个数码相机的存储容量是256 MB 。 如 果每个像素需要3个字节的存储空间,而且一 张照片包括每行1024像素及每列1024像素,那 么这台数码相机可以存多少张照片? 11. 假设一张图片以1024列及768行像素的矩形形 式显示在计算机屏幕上。如果对于每个像素需 要8位来对颜色编码,并用另外8位对亮度编 码,那么整幅图片需要多少字节的存储单元? 12. a . 指出主存储器优于磁盘存储的两个优点。 b . 指出磁盘存储优于主存储器的两个优点。 13. 假设个人计算机上 120 GB 的硬盘只剩下 50 GB 是空闲的,那么用 CD 备份硬盘上的资料是否 合理?用 DVD 呢? 14. 如果磁盘的每一个扇区包含1024个字节,那么 如果每个字符用 Unicode 表示,存储一页文本 (如50行,每行100个字符)需要多少扇区? 15. 用 ASCII 码,每页3500个字符,存储一本400 页的小说需要多少字节的存储空间?如果用 Unicode 又需要多少字节? 16. —个硬盘驱动器每秒钟转360转,那么它的等 待时间是多少? 复习题 49 17. 如果一个硬盘驱动器每秒转360转,寻道时间 是 10 ms , 那么它的平均存取时间是多少? 18. 如果一个打字员每天24小时打字,每分钟打60 个字,那么这个打字员要多久能填满容量是 640 MB 的 CD ? 假定一个单词是5个字符,每 个字符需要1个字节的存储空间。 19. 下面是用 ASCII 编码的信息。内容是什么? 01010111 01101000 01100001 01110100 00100000 01100100 01101111 01100101 01110011 00100000 01101001 01110100 十进制表示。 a. 01111 b. 10100 c. 01100 d. 10000 e. 10110 *31. 将下面的每个十进制表示转换成相应的二进 制补码表示,其中每个值用 7 位表示。 a. 13 b ■—13 c. -1 do e. 16 *32. 假定下面这些位串都是用二进制补码记数法 表示的值,执行下面这些加法运算。辨别哪一 个由于溢出而答案不正确。 a. 00101 b. 11111 c. 01111 +01000 +00001 +00001 00100000 01110011 01100001 01111001 00111111 20. 下面信息使用 ASCII 编码,每个字符一个字 节,并用十六进制记数法表示出来。内容是 什么? 68657861646563696 D 616 C 21. 用 ASCII 对下面的句子编码,每个字符一 字节。 a . Does 100/5^20? b . The total cost is $7.25. 22. 将前面问题的答案用十六进制记数法表示 出来。 23. 列出整数8〜18的二进制表示。 24. a . 分别用 ASCII 码表示2和3,写出数字23。 b . 用二进制表示写出数字23。 25. 什么值的二进制表示只有一个位为1?列出具 有这个特性的最小的6个值的二进制表示。 * 26 . 将下面的每个二进制表示转换成相应的十进 制表不。 a. 1111 b. 0001 C. 10101 d. 1000 e. 10011 f. 000000 g. 1001 h. 10001 i. 100001 j, 11001 k. 11010 1. 11011 *27. 将下面每个十进制表示转换成相应的二进制 表不。 a . 7 b , 11 c . 16 d . 17 e . 31 *28. 将下面的每一个余 16 表示转换成相应的十进 制表 a . 10 0 01 b . 10101 c . 01101 d . 01111 e . 11111 *29. 将下面的每一个十进制表示转换成相应的余 4 表示。 a . 0 b . 3 c . — 2 d •—1 e . 2 *30. 将下面的每个二进制补码表示转换成相应的 d. 10111 e. 11111 f. 00111 +11010 +11111 +01100 *33. 解答下面的每个问 题:将 这些值翻译成二进制 补码记数法(用 5 位模式),转换任何一个减法 运算为相应的加法运算并执行。将所得答案转 换成十进制记数法进行验证。(观察溢出现象。) a . 5 b , 5 c . 12 + 1 - 1 -5 d . 8 e . 12 f . 5 」 +5 -11 *34. 将下面的每个二进制表示转换成相应的十进 制表示。 a . 11.11 b . 100 . 0101 c . 0.1101 d . 1.0 e . 10.01 *35. 用二进制记数法表示下面每个值。 a . 5^ b . 15— c . 5 I 4 16 d . 1 一 e . 6 — *36 .用图 1-26 所示的浮点格式对下面的位模式解 a. 01011001 b. 11001000 c. 10101100 d. 00111001 *37. 用图 1-26 所示的 8 位浮点格式对下面的值编 码。指出出现截断误差的每个情形。 a . b . 2 e . - 3 ^ 31 e . —— 32 *38. 假定你不受使用标准化格式的限制,用图 1-26 所示的浮点格式列出所有可以表示数值 I 的 位模式。 *39 ■用图 1-26 所示的8位浮点格式,求可以表示的 最近似于2的平方根的值。如果计算机利用这 50 第 1 章数据存储 一 浮点格式对这个数做平方,实际得到的值 是什么? *40. 用图 1-26 所示的8位浮点格式可以表示的最近 似于&的数值。 *41. 当米制系统的度量用浮点记数法记录时,解释 为什么会产生误差?例如,若 110 cm 用米制单 元记录情况会怎样? *42. 位模式01011和11011表示同一个值,一个用余 16记数法存储,另外一个用二进制补码记数法 存储。 a . 共周表示的这个值是什么? b . 同一个值分别用二进制补码记数法和余码 记数法存储,并且这两个系统采用相同的 位模式长度,那么表示此值的这两种模式 是一种什么关系? *43. 3个位模式 10000010、01101000和00000010表 示同一个值,分别是采用二进制补码记数法, 余码记数法和图 1-26 所示的8位浮点格式,但 并不是必须按照上面顺序 一一 对应。那么它们 共同表示的值是什么?哪个模式对应哪个记 数法? *44. 在下面的值中,哪一个不能用图 1-26 所示的浮 点格式精确地表示出来? 6 - 2 b . 13 16 d . \ 1 _ 32 ^5 16 *45. 用二进制表示整数的位串长度由4变成6,那么 能够表示的最大整数值会发生什么变化?用 二进制补码记数法又将如何呢? *46. —个存储器容量是4 MB , 每个单元可以存储1 字节,那么其最大地址的十六制表示是什么? *47. 使用 LZW 压缩,并且最初的字典是 X 、少和一 个空格(如 1.8 节所述 ), 那么下面的信息如何 *48 .下面信息使用 LZW 压缩,其字典的第〗、2和3 个条目分别是 x 、 y 和空格。解压缩这条 信息: 22123113431213536 *49. 如果信息 xxy yyx xxy xxyy 用 LZW 压缩,最初字典的第1、2和3个条目分 别是 X 、_^和空格。那么最后的字典条目是什 么? *50 .我们将在下一章学到,通过传统电话系统传 输位的一种方法是,首先将位模式转换成声 音,通过电话线传输声音,接着再将声音转 换成位模式。这种技术的传输速率最多可达 57.6 Kbit / s 。那么如果视频釆用 MPEG 压缩, 这是否能满足远程会议的需要? *51 .使用 ASCII 码对下面的句子编码,并使用偶校 验。在每个字符编码的高位端添加一个校验位。 a . Does 100/5=20? b . The total cost is $7.25. *52. 下面信息的每一个短位串,最初都是用奇校验 传输的。那么哪一个位串绝对出现了差错? 11001 11011 10110 00000 11111 10001 10101 00100 01110 *53. 假如一个24位的编码是这样产 生的: 复制3个符 号连续的 ASCII 编码,并用它们表示每个符号 (例如,符号 A 用位串010000010100000101000001 表示)。这个新编码有哪些纠错特性? *54. 用图 1-30 的纠错码对下面的词语解码。 a . 111010 110110 b . 101000 100110 001100 C. 011101000110000000 010100 d . 010010 001000 001110 101111 000000 110111 100110 编码? e . 010011 000000 101001 100110 社会问— 下面的问题有助于分析一些与计算领域相关的伦理、社会和法律问题。回答出这些问题还 不够,还应该考虑为什么这样回答,以及你的判断是否对每个问题都标准如一。 1. 某个截断错误出现在一个关键时刻,.引起了巨大的损失和人身伤亡。如果有人需要对此 负责,那么是谁?是硬件设计者,软件设计者,编写那段程序的程序员,还是决定在那 个特定应用中使用这个软件的人?如果最初设计这个软件的公司已经修正过这个软件, 但是用户还没有购买这个升级版就用于这个关键应用中,又将如何?如果这个软件是盗 版的,又将如何? 课外阅读 51 2. 一个人幵发的应用忽略了截断错误的可能性及它们的后果,这是可以接受的吗? 3. 如果在20世纪70年代只用2个数字开发表示年的软件(例如用76表示1976年),而忽@了 这个软件在即将到来的世纪之交会失效,这符合伦理道德吗?若今天只用3个数字表示年 (例如用982表示1982,用015表示 2015) 又是否符合伦理呢?如果只用4个数字呢? 4. 许多人认为,对信息进行编码经常会削弱或歪曲该信息,因为这实质上迫使信息必须被 量化。他们认为,若一份调查问卷每题给出了5个等级,并要求回答者按照此标准表达意 见,那这份问卷本身就是无效的。信息可以量化到什么程度?垃圾处理场选址的利弊可 以量化吗?关于核能源及核废料的辩论是可量化的吗?将结论建立于平均值和其他统计 分析上的做法危险吗?如果新闻通讯社在报导调查结果时不使用问题的准确措词,这合 乎道德吗?能够量化一个人的生命值吗?假设一个公司要停止对一个产品改进的投资, 尽管知道附加投资可以减少产品使用的危险性,这合理吗? 5. 在收集和散发数据的权利上,是否根据数据的形式有所差别?也就是说,收集和散发照 片、音频或者视频的权利是否与收集和散发文本一样? 6. 无论有意还是无意,记者的报道通常反映了自己的倾向。通常只要改几个词语,一篇报 道就可能被赋予正面或负面的含义。(比 较: “大多数被调查的人都反对公民投票权”,“被 调查人中有相当一部分支持公民投票权”。)修改一篇报道(回避某些观点或者仔细选词) 和修改一张照片有区别吗? 7-假设用数据压缩系统后导致一些微小但很重要的信息丢失了。这会产生什么样的责任问 题?怎么解决? 课外阅读 Drew , M . and Z . Li . Fundamentals of Multimedia. Upper Saddle River , NJ : Prentice - Hall , 2004. Halsall , F . Multimedia Communications. Boston , MA : Addison - Wesley , 2001. Hamacher , V . C . 5 Z . G . Vranesic , and S . G . Zaky . Computer Organization , 5 th ed . New York : McGraw - Hill , 2002. Knuth , D . E . The Art of Computer Programming, vol . 2, 3 rd ed . Boston , MA : Addison - Wesley , 1998. Long , B . Complete Digital Photography, 3 rd ed . Hingham , MA : Charles River Media , 2005. Miano , J . Compressed Image File Formats ‘ New York : ACM Press ,1999_ Petzold , C . CODE: The Hidden Language of Computer Hardware and Software. Redman , WA : Microsoft Press , 2000. Salomon , D . Data Compression: The Complete Refemce^ 4 th ed . New York : Springer , 2007. Sayood , K . Introduction to Data Compression, 3 rd ed , San Francisco : Morgan Kaufinann , 2005. 数据操控 t 章学习计算机如何操控数据以及如何与外围设备(如打印机和键盘)通信。为此, 我们将研究计算机体系结构的基础知识,学习计算机是如何利用称为机器语言指令 的编码指令来进行编程工作的。 本章内容 2.1 计算机振系結构 ' 2.2 机器语言 2.3 程序执行 *2.4 算不 / 逻袢指令 与其他设备遠信 在第1章中,我们学习了有关计算机数据存储的主题,本章将介绍计算机如何操控这些数据, 其内容包括数据在不同位置间的移动,以及诸如算术计算、文本编辑和图像处理等的操作。首 先我们要了解除数据存储系统之外的计算机体系结构。 广:迎咖扣◊.哑◊.亚视现斤 g 切卽卿踩:: :: 娜」::闽? 盱秘 k a 2.1 计算机体系结构 ___ 计算机中控制数据操控的电路称为 CPU (Central Processing Unit , 中央处理器,通常简称为 处理器)。在20世纪中期, CPU 属于大部件,由若干机架中的电子线路组成,这也反映了该部件 的重要性。不过,科技进步已经极大地缩小了这些部件。今天, PC 机和笔记本电脑中的 CPU 都是 很小的正方形薄片(大约都只有 2 X 2 英寸),它们的引脚插在计算机主电路板[称 为主板 ( motherboard )] 的插座上。在智能手机、小型笔记本电脑和其他 MID (Mobile IntemetDevice , 移 动因特网设备)上, CPU 大约是邮票的一半大小。由于它们的尺寸比较小,这些处理器被称作微 处理器 ( microprocessor )。 2.1.1 CPU 基础知识 CPU 由3部分构成(图 2-1): 算术/逻辑单元 (aritbmeticAogic unit ), 它包含在数据上执行运 算(如加法和减法)的 电路; 控制单元 (control unit ), 它包含协调机器活动的 电路; 寄存器单 元 ( registerunit ), 它包含称为 寄存器 ( register ) 的数据存储单元(与主存单元相似),用作 CPU 内部的信息临时存储。 寄存器单元中的一些寄存器被看成 是通用寄存器 ( general-purpose register ) ,而其他一些则 被看成 是专用寄存器 ( special-purpose register ) „我们将在 2.3 节讨论一些专用寄存器,现在我们 只关注通用寄存器。 *2.6 其他体系结构, 复习题 1 社会问题 诔外阅读 从存储器中取出一个要加的值放入一个寄存器中 D 从存储器中取出另一个要加的值放入另一个寄存器中。 激活加法电路,以步骤1和2所用的寄存器作为输入,用另一个寄存器存放相加的结果。 将结果存入存储器。 停止。 图 2-2 王存储器中的值相加 2.1.2 存储程序概念 早期计算机不是很灵活,每个设备所执行的步骤都被内置于控制单元中,作为计算机的一 部分。为了增加其灵活性,早期电子计算机的设计使得 CPU 可以方便地重新布线。其灵活性通 过插拔装置体现,类似于老式的电话交换台上把跳线的端子插到接线孔中。 认识到一个程序可以像数据一样进行编码并存储在主存储器中,这是一个突破性进展(但 是将其归功于约翰•冯•诺依曼显然是不正确的)。如果控制单元可以从存储器中读取程序、将 指令解码并执行它们,那么机器要遵照执行的程序就可以被修改,这只需要改变计算机存储器 中的内容而不必对 CPU 进行重新布线。 2.1 计算机体系结构 53 CPU 主存储器 餘逻 辑单元 控制单元 图 2-1 通过总线连接的 CPU 和主存储器 通用寄存器用于临时存储 CPU 正在操控的数据。这些寄存器存储算术/逻辑单元电路的输入 值以及该部件所产生的结果。为了操作存储在主存储器中的数据,控制单元要把存储器里的数 据传送到通用寄存器,通知算术/逻辑单元由哪些寄存器保存了这一数据,激活算术/逻辑单元中 的有关电路,并告知算术/逻辑单元哪个寄存器将接收结果。 为了传输位模式,计算机 CPU 和主存储器通过一组称为总线 ( bus , 图 2-1) 的线路进行连 接。利用总线, CPU 给出相关存储单元的地址以及相应的电信号(告知存储器电路,将在指定 单元中获取数据),从主存储器中取(读)出数据,同理, CPU 可以向主存储器中放入(写入) 数据,方法是提供目的单元地址和一起写入的数据以及适当的电信号(告知主存储器,将要储 存发送给它的数据)。 基于此设计,将存储在主存储器中的两个值相加的任务不仅仅涉及执行加法运算。数据必 须先从主存储器传输到 CPU 的寄存器中,值相加后,把结果放置在寄存器中,然后再把结果存 储到主存单元中。整个过程被总结成如图 2-2 所示的5个步骤。 ______ I-! □ : ,口 ::: ':"I ": ;'"I • ...... ...ui,、.... ' ' ' S' • •••••• r 吕 总线 I_I 寄存器 骤骤骤骤骤 步步步步步 54 第 2 章数据操控 将计算机存储设备与其对应功能进行比较是很有启发性的。寄辱義两于存储可立即进行 运算的数据,主存储器用于存储即将使用的 数据, 海量存储器用于存储最近也许不会使用的 数据。许多计算机设计增加了一个附加的存储器层次,称为高速缓斤每餺聲、高速缓冲存储 器 (cache memory ) 是位于 CPU 内部的高速存储器的一部分(也许有及茸 KB :)。 在这个特殊 的存储区域中,计算机试图保存主存储器中当前最重要的那部分内容的一个副本。这样,通 常要在寄存器与主存储器之间进行的数擔传输将变成寄存器与高速缓冲存储器之间的数据传 输 9 因此,高速缓冲存储器中的任何改变都会在恰当时间被一^传输给主存储器。于是 , GPU 可以较快地执行它的机器周期,因为它不会被与主存储器的通信所延迟: 广:.货# i 适洁 ::邊 •濟「:君.::芬.茲::: .VI. V. •; : .L .V. X-.. w] . cr r - ' sr-i - r r 一 s . t .一 v .—一.一一 ........ /:y : :■.夂 , 心 4 通 ㈣ 海鶴樣闘.綱 将某项发明的荣誉授手个乂辱是爷受争议 A 人们将白炽灯的发明归功于托马岬 • 愛遴生 ( Thomas Bdison ), 僞奉其坪知卑: 员也, 曾所制^編 坪典, 私某种韋义上说,爱迪^只是班 : 鍊4运地 获得了 专利。人如木‘是莱特 ( WrigM ) 1 克聲 是明 1 的飞机、但他们曾与其他人堯爭 ; 并*益于 其他人的研究,,在 桌种程 度上,他们又被达芬♦抢先 1 了,他羊在15世纪就有士疏玩 飞行机葬的想法。甚至达芬奇的设计看起来也是假借前人的思想。当然对于这些发明,被 认定的发明人还是有权拥有被授予的荣誉的。但对于其他一些情况, 历 史上的有些荣誉授予 似乎是系拾:当的,例如存错程序的概念。毫无疑问,约翰 •冯. 诺依曼 4 fohji von Neumann ) 是一位卓 越的科 学家,理应为自己的许多贡献获得荣誉。但是,历史选择授予他荣誉的贡献 是存储程參換念,但这一思想很显然是忐宾夕法尼亚大学莫尔电子玉程夢院以埃克特 ( J . R Eckert ) 为首的研究人员提出的.约翰•冯 • 诺依曼不过是第一个在著作中转迷这一思想的 人,因此计算机界选择他作为发明人。 将计算机程序存储在主存储器中的思想称为存储程序概念 ( stored-program concept ) , 它已经 成为今天所使用的标准方法。最初的困难源于人们将程序和数据视为不同的 实体: 数据存储在存 储器中,而程序为 CPU 的一部分。于是就成了 “只见树木,不见森林”的一个最好实例。人们很 容易被老一套所束缚,如果今天仍不了解这一点,那么计算机科学的发展也许还会裹足不前。的 确,科学中令人兴奋的部分是,新的思想不断为新的理论和新的应用开启大门。 问齡 騎 1. 梅_算机中一个存储眷元的:内容移到另一个存储单元 ,你 认为需要哪些事件序列? 2, 刺__个值写入存 储单宛 中,:那么 CMJ 賛愛提供给主存储器电路什么信息? : 3;;審聾赛樓齬卜羞畚祕_ 以及通 用奢存器鄺是¥储赛统。.它们在用 Si 有什么不同? ! 卜 ...:i ' r :; ..:. . . : .. .. 丨 . ,: M i. : : “ :; :; . : r.:' i, -- : :i :■=■:■. ■: . 4 ;..」■.. ■: :.: : .-.杂::.-.:々:.:二.-.. -:;■ :;. - ■:■ : ■;;, .. .I : . : 〜.. : . :: . : ..〜. : ..心似. 2.2 机器语言 为了应用存储程序概念, CPU 被设计成可以识别二进制模式编码的指令。这组指令以及编 码系统统称为机 器语言 (machine language )。 使用此语言表达的指令称为机器级指令,更多人 称其为机 器指令 Cmachine instruction ) Q 2.2.1 指令系统 一 个典型 CPU 必须能够解码及执行的机器指令列表非常短。实际上,一旦计算机能够实现 2.2 机器语言 55 某些基本但适当的任务,那么添加再多的特性也不会增加该计算机理论上的能力。换句话说, 超过某个特定点之后,附加的特性也许能够增加诸如便利性等能力,但是不能增加该计算机的 基本能力。 在设计计算机时,该事实将被利用到何种程度导致了两种 CPU 体系结构的出现。 一 方面是 CPU 只需要执行最小的机器指令集,因此产生了 RISC (Reduced Instruction Set Computer , 精简指 令集计算机)。 RISC 的支持者认为,这样设计的计算机效率高、速度快,而且制造起来较便宜。 另一方面,另外一些人则认为 CPU 应能够执行大量复杂的指令,尽管许多在技术上是多余的, 因此产生了 CISC (Complex Instruction Set Computer , 复杂指令集计算机)。 CISC 的支持者认为, CPU 越复杂越容易应对今天日益增加的软件复杂性。通过 CISC , 程序可以利用一组功能强大的 丰富的指令集,其中很多指令所能实现的任务需要 RISC 设计中的一个多指令序列才能实现。 20世纪90年代至21世纪,市场上有售的 CISC 和 RISC 处理器在积极争抢桌面计算领域。用于 个人计算机中的英特尔处理器是 CISC 体系结构的代表,而 PowerPC 处理器(由苹果、 IBM 和摩托 罗拉公司联合开发)是 RISC 体系结构的代表,被用于苹果 Macintosh 中。随着时间的推移 , CISC 的制造成本大大降低了,因此英特尔的处理器(或 AMD 公司的处理器)现已几乎遍及台式机和笔 记本电脑,甚至苹果公司生产的计算机也开始使用英特尔公司的产品。 虽然 CISC 在台式计算机领域站稳了脚跟,但其电耗非常大。与此相反, ARM(Advanced RISC Machine ) 公司专门针对低电耗设计了一种 RISC 体系节构。 ( ARM 的前身是 Acorn 计算机公司, 现在是 ARM Holdings 。) 因此, ARM 处理器(由多个供应商制造,包括髙通和德州仪器)已出 现在游戏控制器、数字电视、导航系统、汽车部件、移动电话、智能手机和其他消费性电子产 品中。 不管选择 RISC 还是 CISC , 机器指令可以分为3 类: (1) 数据传 输类; (2) 算术/逻辑 类; (3) 控制类。 1. 数据传输类 数据传输类指令包含请求在各个位置之间传输数据的指令,图 2-2 中的步骤1、2和4都属于 这一类。需要注意的是,使用传输 ( transfer ) 或移动 ( move ) 来标识这组指令实际上是用词不 恰当的。因为传输的数据很少被从原始位置檫除。执行传输指令的过程更像是复制数据而不是 移动数据,因此,复制 ( copy ) 或克隆 ( clone ) 能更好地描述这组指令的活动。 关于术语,我们应注意到,提到 CTU 与主存储器之间数据的传输时,有专门的术语。用存 储单元的内容填充通用寄存器的请求通常称为 LOAD (加载) 指令; 相反,将寄存器中内容传 输给存储单元的请求称为 STORE (存储)指令。在图 2-2 中,步骤1和2是 LOAD 指令,步骤4是 STORE 指令。 在数据传输类中有这样一组重要的指令,即与 CPU - 主存储器环境之外的设备(打印机、键 盘、显示器以及磁盘驱动器等)通信的指令。这些指令处理该机器的输入/输出活动 0/0), 因 此被称为 I/O 指令, 且有时因为其特别而被单独归为一类。另一方面, 2.5 节将介绍这些 I / O 活动 如何利用与请求在 CPU 及主存储器之间传输数据同样的指令来完成操作。因此,我们将 I / O 指令 归入数据传输类指令。 2. 算术/逻辑类 算术/逻辑类指令告诉控制单元请求在算术/逻辑单元内实现一个活动。图 2-2 中的步骤3属于 这一类。正如其名称所示,算术/逻辑单元还能够执行基本算术运算之外的运算。在这些附加的 运算中,就有第1章中介绍的布尔运算与、或和异或,本章后面将进一步讲解。 大多数算术/逻辑单元中可以用另外一组运算进行寄存器中内容的左右移动。这些运算称为 移位 ( SHIFT ) 运算或循环移位 ( ROTATE ) 运算,前者丢弃一端“移出的位”,而后者将它们 放到另一端留出的空位上。 56 第 2 章数据操控 把存储器中的一个值加载到一个寄存器中。 把存储器中的另一个值加载到另一个寄存器。 如果第二个值为0,那么转移到步骤6。 第一个寄存器中的值除以第二个寄存器的值,结果留在第三个寄存器中。 把第三个寄存器的值存储到存 储器。 停止。 图 2-3 存储器中数值的除法运算 2.2.2 — 种演示用的机器语言 现在让我们来分析一台典型的计算机的指令是如何编码的。我们用于讨论的计算机在附录 C 中描述,其体系结构见图24。它有16个通用寄存器,256个主存储器单元,每个存储单元容量 为8位。为了便于参考,我们将寄存器标号为数值0〜15,把存储单元的地址编为数值0〜255。为 方便起见,我们将这些标号及地址看做以二进制表示的数值,并使用十六进制记数法压缩它们 的位模式。于是,寄存器标号为0〜 F , 存储单元的地址为00〜 FF 。 CPU 主存储器 图 2-4 附录 C 描述的计算机的体系结构 机器指令编码形式包括两部分,即 操作码 (operation code , 缩写为 op - code ) 字段和 操作数 ( operand ) 字段。操作码字段中的位模式指明该指令要求的是什么基本运算,如 STORE 、 SHIFT 、 X 0 R 和 JUMP 等。操作数字段中的位模式提供操作码指定运算的更详细信息。以 STORE 操作为 例,其操作数字段中的信息指示哪个寄存器包含将被存储的数据,哪个存储单元用于接收该 数据。 我们演示用的计算机(见附录 C ) 的整个机器语言只包含12条基本指令。每条指令都用16位 编码,由4个十六进制数字表示(见图2-5)。每条指令的操作码由前4位组成,等价于第一个十 六进制数字。注意(见附录 C ), 这些操作码用十六进制数字1〜 C 表示。特别是,附录 C 中的表说 明以十六进制数字3起始的指令表示 STORE 指令,以十六进制 A 起始的指令表示 ROTATE 指令。 3. 控制类 控制类指令包含指导程序执行而非数据操作的指令。图 2-2 中的步骤5属于此类,但它是一 个很初级的例子。这一类包括计算机指令系统中许多比较有趣的指令,例如 JUMP (转移)或 BRANCH (分支)系列指令用于指示 CPU 执行非列表中下一条指令的指令。转移指令有两种, 即 无条件转移 ( unconditionaljump ) 和条件转移 ( conditionaljump ) 0 前者的例子如“跳转到步 骤5”,后者的例子如“如果所得数值为0,跳转到步骤5”。两者的区别是,只有满足某个条件时, 条件转移才会引起“地点改变”。举例说明,图 2-3 中的指令序列是表示两个数值相除的算法, 其中步骤3是条件转移,用以防止除数为0。 &K. ^K. 0K. 頭弱撰 邏骚骑 」1\ - 二\ 二\ 二\ iK 一 7 2.2 机器语言 57 ,先了:筒化文唯的解夢,本章杀 W 所用的机諸#嘗(在轉_中有 辦球 ㉔ ■械有栺令都 使用 了固定 的大小 ( 2 个字节)。因此,为界得一个推令, au 蓐裏 f 秦&索棘连繹存赌单元 絝内:蓉,¥▲给其器加1这_贯了敢指令器的特性' 爲:雨?:对于 asc ^-:_^ 畢译言, 指令长 度可变 d 例如:,令天均赛___器措♦长疼有大 省 W 、, 小的只螓1孛籴,來妗 要彡个 字节,这取决于该指令的确切 ㈤ 用这样^语言的 ,(5?11根#鵠令捧#码參—定所引入指令的|长渡 |1 。 ; 也 就是说 ;_. : C ? U 首^取指本的操作:鸠;然后 基于收到的位模式得知,要得到佘下的指令还需要从存储器中取多少字节。_ / 操作码 操作数 ~ I I 1 QQ 11 01.1 1010 0111实际的位模式 (16 位) 3 5 A 7 十六进制形式 (4 个数字) 图 2-5 —条指令的组成(附录 C 描述的计算机) 在我们演示用的计算机中,每条指令的操作数字段由3个十六进制数字 (12 位)组成,在每 种情况下对操作码给定的通用指令做了进一步澄清 ( HALT 指令除外,因为它不需要进一步的规 定)。例如(见图2-6),如果一条指令的第一个十六进制数字为3 (存储寄存器中内容的操作码), 那么该指令的下一个十六进制数字则指出哪个寄存器中的内容需要存储,而最后的两个十六进 制数字则指出由哪个存储单元接收该数据。因此,指令 35 A 7 (十六进制数)是指“将第5寄存 器中的位模式存储 ( STORE ) 到地址为 A 7 的存储单元”。(注意使用十六进制记数法是如何简化 我们的讨论的。事实上,指令 35 A 7 是指位模式0011010110100111。) 指令 f 德 $■ ::羞 7 SSSHlSii 寄 \ 操作数的这部分标识接收 个存储单元中 \ 数据的存储单元的地 1 址 操作数的这部分标识哪 个寄存器的值需要存储 图 2-6 指令 35 A 7 的译码 (对于主存储器容量为什么以2的幂为度量单位,指令 35 A 7 同样提供了一个明晰的例子。因 为该指令保留8位用于指定该指令所用的存储单元,所以能够准确地引用2 8 个不同的存储单元。 因此我们需要用这么多存储单元构建1个主存储器——地址从0〜255。如果主存储器有更多的存 储单元,我们就不能够写出指令以区别 它们; 如果主存储器的存储单元比较少,我们写出的指 令就可能引用不存在的存储单元。) 再举一个例子说明操作数字段如何阐明操作码给定的通用指令。我们来考虑一个操作码为7 (十六进制数)的指令,它请求将两个寄存器的内容进行 OR (或)运算。(在 2.4 节中,我们会 知道两个寄存器的“或”运算意味着什么。现在我们感兴趣的只是指令是如何译码的。)在这种 情况下,下一个十六进制数字将指示存放运算结果的寄存器,而操作数最后两个十六进制数字 58 第 2 章数据操控 指示要对哪两个寄存器进行“或”运算。因此。指令 70 C 5 指的是“将寄存器 C 和寄存器5的内容 进行‘或’运算,并将结果存入寄存器0”。 ^ 我们所讲的机器的两条 LOAD 指令存在细微的差别。这里,操作码1 (十六进制)表示将某 一存储单元内容载入寄存器的指令,操作码2 (十六进制数)表示一个指令把一个特定的值加载 到寄存器。它们的差别在于,第一种类型的指令操作数字段包含1个地址,第二种指令的操作数 字段包含了 一个要载入的实际位模式。 注意,该机器有两条 ADD (加法)指令,一条用于二进制补码记数法表示的数值相加 ,一 个用于浮点记数法表示的数值相加。把它们区分幵来的原因是,这两种加法在算术/逻辑单元内 部有不同的实现步骤。 我们用图 2-7 结束本节,图 2-7 把图 2-2 中的指令编码为机器语言。我们已经假定,相加的数值以 二进制补码记数法形式存储在存储地址 6 C 和 6 D 中,其相加的结果存放在地址为 6 E 的存储单元里。 指令编码 翻 译 156 C 把地址为 6 C 的存储单元里的位模式载入寄存器5 166 D 把地址为 6 D 的存储单元里的位模式载入寄存器6 5056 把寄存器5和6的内容按二进制补码表示相加,结果存入寄存器0 306 E 把寄存器0的内容存放到地址为 6 E 的存储单元中 C 000 停止 问题与练习 图 2-7 图 2-2 中指令的编码形式 1- 对乎数据在计算机不同位置之间 移勒的 操作,使用“移 ST ( move ) 这=术语为什么是用词不当? 2. __中, JUMP « 春是通 过给出目的地袭称(或步骤号)的方法来表示的 X 例如“跳到_6” ) 。 _方法的缺点是,如果一个指令名称%或步骤号)后_改变了,那么必须寻找所有转移10该指令的 _ ip 指令,并改变这些 jump 指令中的目的地。请另卉设计一种_11^辑令的方法,#得不需要 [3 /指令“如果 :0 麵 $0, 那杳 棘 到步骤 7” 盛无条件转移述是条件转移?为存么; 4. 用实际的缶模式编写图17 中的 示例輕序。 : : : ’ :: 5. 下_令是用附录 C 描述的机器语言編写:的。请用:自然谱言解释这些指聲:^ a . j 6 ^A b . BADE c . 803 C d . 40 F 4 - 6. 在附录 C 描述的机器邊言里 r 指令 154 B 和 25 AB 有什么区别? / 7. T 興是用自然谣言描述的一些指令. 讀把 它们翻译为附藥 C 中描述的机遍语言。 纪. 千六进制数德 兑装入 ( LOAD ) 春器3 中。 b . 将寄存器5循环 ( ROTATE ) 右移3:位。 c . 对寄存器 A 和寄存器5执行与 ( AND ) 操怍,并将其绪果存入寄存器0丰。 2.3 程序执行 计算机总是按照需要把存储器里的指令复制到 CPU 中来执行存储器中的程序。一旦指令到 达 CPU , 每个指令就会被解码及执行。从存储器中取指令的顺序与这些指令存储在存储器中的 顺序相对应,除非被 JUMP 指令更改。 为了理解整个执行过程如何进行,我们有必要仔细了解一下 CPU 内部的2个专用寄存器 ,指 令寄存器 ( instructionregister ) 和程序计数器 (program counter ) (见图2~4)。指令寄存器用于存 2.3 程序执行 59 储正在执行的指令;程序计数器包含下一个待执行指令的地址,因此它用于以机器方式跟踪程 序执行到了什么地方。 CPU 通过不断重复执行一个算法来完成工作,该算法引导它完成一个称为机 器周期 (machine cycle ) 的三步处理。该机器周期的3个步骡分别为取指、译码和执行(图2-8)。在取指 步骤, CPU 根据程序计数器指定的地址,请求主存储器给它提供存放在该地址的指令。因为我 们的计算机的每一条指令长度为2个字节,所以取指过程需要从主存储器中读取2个存储单元的 内容。 CPU 将从存储器中读取的指令存放在指令寄存器中,然后将程序计数器的值加2,使得程 序计数器包含下一条要执行的指令的存储单元地址。这时,程序计数器为下一次取指做好了准备。 图 2-8 机器周期 对指令寄存器中的位模式 进行译码 购买个人电 脑时邇 常用时钟速度来比较计算机 D 针算机 的时钟 Uioek ) 是一个称 为据讀 譜的电蹲,生成用于协调计算机活动的脉冲一该振荡. : 器 ,电路 麥成脉冲 哉味, ' 则机器 周期的 执行速度也越快。时钟速度以赫兹(缩写为 Hz ) 为单位, 1 H 5 湘当于每秒1个周期(或脉冲)。 台式计算机比较典型的时钟周期是几百 MHz (较老的型号)到爲 GHz v (MHz 是 megahem 的 缩写, 1MHz=10 6 Mz, GHz 是 gigahertz 的缩写, 1 GHz=1000 MHz。) 1 不同,的 CPU 设计在一个时#廟期直完啟的 工作量 是不#的:,典 矣比棱 具有 不同; CPU 的计算板时,单单此较时钟速度没有太太的意;义。釦果你在比较两台计算机,一台基于英特 尔处理器, 一 台基于 ARMA 理器,那么 采用基淮测试 ( benchmark) 来进行比较则更有意义。 暴准测试是指,在比聲不巧计算机_让它们执行 同样巧 程序 (# 为基 准),然后比辑它们坤性 通过选择代表不 同类型 雇对的 it 就能够从这些比较对各矣市场有意义的4窠。: 由于指令现在已经存入了指令寄存器, CPU 对该指令译码,其中包括根据该指令的操作码 将操作数字段分解为适当的部分。 然后, CPU 激活相应电路以执行指令,完成所请求的任务。例如,如果该指令是从存储器 中加载, CPU 将给主存储器发送相应信号,等待其发送数据,再将数据存入要求的寄存器;如 果该指令是算术运算, CPU 将激活算术/逻辑单元中相应的电路,并将正确的寄存器作为输入, 等待算术/逻辑单元计算结果并^结果存入相应的寄存器。 一 旦指令寄存器中的指令执行完毕, CPU 又将从取指步骡开始下一个机器周期。注意,由于 60 第 2 章数据操控 程序计数器在前一个取指步骤的最后已经增加了值,所以它再次为 CPU 提供了正确的指令地址。 JUMP 指令的执行比较特殊。例如,指令 B 258 (见图 2-9) 的含义是“如果寄存器2的内容与 寄存器0的内容相同,则跳转到地址为58 (十六进制)的指令”。此时,该机器周期的执行步骤 首先是比较寄存器2和寄存器0。如果它们包括不同的位模式,那么执行步骤结束,并开始下一 个机器周期。不过,如果这两个寄存器内容相同,那么机器在执行此步骡时要将数值58 (十六 进制)存入程序计数器里。在这种情况下,下一个读取指令步骤发现程序计数器值为58,于是 该地址的指令将为下一条被读取和执行的指令。 注意,如果该指令为 B 058, 那么该程序计数器是否需要更改取决于寄存器0和寄存器0的内 容是否相同。但是,它们是相同的寄存器,因此必然有相同的内容。于是,无论寄存器0的内容 是什么,形式为 BOXY 的指令都将使程序跳转至存储位置 XY 并执行。 指令七 B 气 操作码 B 表示,如果指定的 寄存器的内容与寄存器0的 内容相同,那么改变程序计 数器的值 5 另 操作数的这一部分是要放 在程序计数器中的地址 操作数的这部分指示要与 寄存器0进行比较的寄存器 图 2-9 指令 B258 的译码 2-3.1 程序执行的一个例子 让我们从机器周期的角度看图 2-7 的程序是如何执行的。该程序从主存储器中取出两个值, 计算它们的和,并将结果存储在一个主存储单元里。首先,我们需要将该程序存放在存储器的 某个地方。对于这个例子,假设该程序存放在从 A 0 (十六进制)开始的连续地址中。在按照这 种方法存放好该程序后,要执行这个程序只需要把该程序的第一条指令的地址 ( A 0) 存放在程 序计数器中并开启机器(见图2-10)。 程序计数器包含第 一条指令的地址 CPU 主存储器 I :懲 程序 W 数器 1 剀 总线 地址 单元 A0 A1 程序被存放 A2 as _ 在从 AO 开始 A3 … \ 的主存储器 地址里 A4 ! 50 A5 fss j A6 iES ] A : 7 .. /' A8 1 co ~: A9 j o cn _ 图 2-10 图 2-7 中的程序被存储在主存储器中并准备执行 2.3 程序执行 61 CPU 开始机器周期的取指步骤,该步骤把存放在地址 A 0 的指令从主存储器中取出,并将该 指令 (1560 放入指令寄存器里(图 2- lla )。 注意, 在我们的计算机里指令的长度为16位 (2 个字 节)。 于是,要读取的整个指令占用了存储单元2个地址,即 A 0 和 Al 。 CPU 的设计考虑到了 这点,使得取指时能够读两个存储单元的内容,并将获得的位模式存放在长度为16位的指令寄 存器中。接着, CPU 给程序计数器加2,使得该寄存器包含下一条指令的地址(图 2- llb )。 在第 一个机器周期的取指步骤结束时,程序计数器和指令寄存器包含下列的 数据: 程序计数器: A 2 指令寄存器: 156 C cpu 主存储器 单兀 ; " ; i6d ( a ) 取指步骤开始时,从存储器中取出从地址 A 0 开始的指令,放在指令寄存器中 CPU 主存储器 程序计数器 mm 地址 单元 总线 _ [ : is! j 指令寄存器 A1 i§C^j A2 麵 . . .:: .I:,, A3 :. n... … 漏 . ( b ) 然后增加程序计数器的值,使它指向下一条指令 图 2-11 执行机器周期的读取步骤 ^然后, CPU 要分析指令寄存器中的指令,并得出 结论: 它要把地址为 6 C 的存储单元的内容 加载到寄存器5中。该加载工作是在机器周期的执行步骤完成的,接着 CPU 开始下一个机器周期。 这个周期首先要从以地址 A 2 开始的2个存储单元中取指令 166 D 。 CPU 要将此指令存入指令 寄存器,并将程序计数器增加为 A 4。 因此,此时的程序计数器和指令寄存器中的值 如下: 程序计数器: A 4 指令寄存器: 166 D 现在, CPU 对指令 166 D 进行译码,确定它要将地址为 6 D 的存储单元的内容加载到寄存器6, 然后执行该指令。这时候,寄存器6才真正加载了数据。 因为该程序计数器现在的值是 A 4, 所以 CRJ 从这个地址开始取下一条指令。于是,指令5056 被放入指令寄存器,程序计数器增加为 A 6。 现在, CPU 对指令寄存器的内容进行译码,然后激 活二进制补码加法电路,以寄存器5和寄存器6作为输入执行加法运算。 在该执行步骤中,算术/逻辑单元执行所请求的加法运算,将结果存入寄存器0 (如控制单 元所要求的那样),然后向控制单元报告它已经完成了任务。然后 CPU 开始另一个机器周期,再 一 次借助程序计数器,从以地址 A 6 开始的2个存储单元中取下一条指令 (306 E ), 然后将程序计 62 第 2 章数据操控 数器增加到 A 8, 接着译码并执行该指令。此时,和已经被放在了存储单元 6 E 中。 下一条指令从存储单元 A 8 开始读取,同时程序计数器被增加到 AA 。 指令寄存器 ( C 000) 的内容现在被译码为停止指令。因此,机器在该机器周期的执行步骤停止,程序完成。 概括地说,如果我们遵照程序详细的指令列表,那么一个存放在存储器里的程序的执行过 程就如同你我都可以做的那样。我们可以通过标记指令来确定执行到的位置,而 CPU 则利用程 序计数器来确定执行到的位置。在确定下一步要执行什么指令后,我们需要读该指令并分析它 的含义,然后实现所请求的任务并返回到指令的列表,为下一条指令做准备。同样,计算机执 行指令寄存器中的指令,然后从另一个取指步骤开始继续进行。 2.3.2 程序与数据 许多程序可以同时存储在计算机的主存储器里,只要它们的地址不同。开启计算机时运行 哪个程序可以通过适当地设置程序计数器来决定。 然而,我们必须 记住: 数据也存储在主存储器中,也用0和1来编码,所以计算机自己无法 知道哪是数据,哪是程序。如果程序计数器被赋予了数据的地址而非所希望的程序的地址,那 么在没有更好选择的情况下,该 CPU 会像取指令一样读取此数据的位模式并执行。最终的结果 取决于该数据。 然而,我们不应该就此得出结论,以为将程序和数据以相同的形式存入计算机的存储器是 错误的。事实上,这已经被证明是一个有用的特性,因为它使得某一个程序可以操控其他程序 (甚至是自己),就像它可以操控数据一样。由此可以设想有这样的一个程序,它可以根据其与 环境的交互自我修正,因此展现了它的学习 能力; 亦或者有这样一个程序,它可以编写及执行 其他程序,以解决它所遇到的问题。 问题与练京: • — - - • • .•- — .. . ... — . 一 . . - ■ -• ... _ .. 一 . _ . . - - - ••- .._••• .... - - .. • • • • •• - • •- : — 1•假设在附录 C 描述的计算机中,从地_恥||1 離的着褚单 元中包含下列(卡方迸聯-位 模式: ... — . - « 圓 \ _ 圓圓 ■■圓 地址 -内容 - 00 / 14 , - : ■ 01 02 02 34 03 ;/ 17 04 C0 05 00 如果启动计算机时,程序计数華被设为 00, 那么读计算机停止时,地址为 17 ( 十六进制 .) 的存储单元 里会有什么样的 & 模 式?, . 1 V' ■ ' ,: M "' : ■; I ;;;;,; ^ 假墀在附录 C 描 述的计 算机十,,林喊址 B 0 到埤的存傅单筘屮包含下邦 ( 十六进制)位 模式: 地址 内"容' : ::::':: ::'::X ;:..:. i :,;: i :';| .. |:..'| i : : :. i . ! i .: *2.4 算术 / 逻辑指令 63 如果启动计算机时程序计数器含有值 F 0, 当到达地址为 F 8 的指令肘,该计算机执行什么? —.. •.■ mw --- V.--.-V , :_ r . v .-.-.-.-.-.- jr .- - ,-:: *2.4 算术/逻辑指令 ; a . 如果程 序诽数 #最:衡为执行第一条指令之煞:::聲饍在會__吟 ffi 模式*作鳴?: b . 在执行停止指令时,存储单元 BS 里的位模式是什么? 3.假设在附录 C 描述的计算机中,从地址 A 4 到 B 1 的存储单元中包含下列(十六进制)位 模式: 地址 内容 A4 20 A5 GO , A6 21 ;: 遍 . .: 03 i A8 ,: m A9 m AA B1 AB BO AC 50 AD 02 AE BO . ...A: ... AA m . Gfe , , i. m .■- •- . ,, m .斗 i 假设读_机肩離时程序计数器含有值回答下面的问题。 a . 地址为 AA 的指令第一次执抒时,寄存器0中是什么 I b . tt_AA 的指令第二次执行时,寄存器0中是什么? c . 该计算机停止之前,存放在地址 AA 里的指令执行了多少次? _ 假谗在盼录 q 描述@计箅 ㈣ 扣,从,捵_印孑 9 的裔储萬萍中包含遊麵:出宍趣譜):位樺式; 如前所述,算术/逻辑指令组由算术、逻辑、移位等运算指令组成。在本节中,我们将详细 介绍这些运算。 2.4.1 逻辑运算 第1章介绍了逻辑运算 AND (与 )、 OR (或)和 XOR (异或),它们都组合两个输入的二进 制位,得到一个输出的二进制位。这些运算可以扩展成为这样的 运算: 组合两个二进制位串产 生一个二进制位串输出,只需要把上述基本运算应用到每一列。例如,位模式10011010与 容 内 也 ■20.G030.F 8 26OOMF9FFFF g Fl. F2 .F3 T E4F5 F6 .:g F8, .F.9; 64 第 2 章数据操控 11001001进行与 ( AND ) 运算所得结果 如下: 10011010 . AND 11001001 10001000 ' 这里,我们只是在每一列上将两个二进制位进行 AND 的结果作为结果。同理,将这些位模式进 行 OR 运算及 XOR 运算时 得到: - 10Q11010 10011010 OR 11001001 XQR 11001001 11011011 01010011 AND 运算的一个主要用途是将位模式的一部分设为0,而不影响另外一部分。例如,如果 字节00001111是 AND 运算的第一个操作数,那么会产生什么结果?即使不知道第二个操作数的 内容,我们仍然能够得出这样的结论,即结果的最高4位均为0。其次,结果的最低4位将与第二 个操作数的最低4位相同,如下 所示: 00001111 AND 10101010 00001010 AND 运算的这个应用是一个称为屏蔽 ( masking ) 的过程的例子。这里,一个称为掩码 ( mask ) 的操作数决定另一个操作数的哪个部分会影响结果。在这个 AND 运算中,屏蔽得出的结果中, 其中一部分是一个操作数的复制品,而没有复制的部分为0。 此类运算在操作一个位图 (bit map ) 时很实用。位图是由若干二进制位组成的串,其中每 个位表示一个特定对象存在与否。在讨论图像表示的时候已经涉及位图,那时每一位是与一个 像素相联系的。再举一例,一个由52位组成的串,其中每个位与一张特定的扑克牌相联系,那 么此位串可以表示一手5张牌,我们只需要将1赋予和手中牌相对应的5个位,而将0赋予其他位。 同理,13个位为1的52位位图可以表示一手桥牌,32位位图可以表示32种口味冰激凌的有无。 接着,假设一个8位存储单元被用作一个位图,我们希望查明与从高位位算起的第3位相联 系的对象是否存在?我们只需要将整个字节与掩码00100000进行 AND 操作,当且仅当该位图从 髙位端算起的第3位本身为0时,结果的字节值全为0。于是,在该 AND 运算中安排一个条件转 移指令,程序就可以实现相应的动作。其次,如果该位图从高位算起的第3位为1,我们想在不 破坏其他位的情况不将其改为0,那么可以把该位图与掩码11011111进行 AND 操作,然后用结果 替代原来的位图。 AND 运算可用于复制一个位串的一部分,方法是在不复制的部分置0,而 ORg 算也可用于 复制一个串的一部分,但在不复制的部分置1。为此,我们再次使用掩码,但是这次用0来指示 要复制的位的位置,用1指示不复制的位置。例如,将任何字节和11110000进行 OR 运算,结果 的位模式的最高有效4位为1,而剩余位则是另外一个操作数最低有效4位的副本,如下 所示: 11110000 OR 10101010 11111010 因此,掩码11011111参与 AND 运算一定可以使得一个8位位图最髙起第3位变为0,而掩码00100000 可以参与 OR 运算将同样的位置变为1。 XOR 运算的一个主要用途是形成一个位串的反码。将任意一个字节与全部为1的掩码进行 XOR 运算可以得到该字节的反码。例如,注意下面示例中第二个操作数与其结果的 关系: *2.4 算术 / 逻辑指令 65 11111111 . XOR 1Q1Q1010 01010101 在附录 C 描述的计算机语言中,操作码7、8和9分别用于逻辑 OR 、 AND 和 XOR 。 每个操作 码要求在指定的2个寄存器内容之间进行相应的逻辑运算,并将结果存放在另一个指定的寄存器 中。例如,指令 7 ABC 要求 的是: 将寄存器 B 和 C 的内容进行 ORil 算,并将结果存放在寄存器 A 中。 2.4.2 循环移位及移位运算 循环及移位运算用于移动寄存器内的二进制位,常用于解决对齐问题。这些运算根据移动 方向(向右或向左)和其过程是否循环来分类。这些分类准则的混合使用产生了许许多多变体。 下面我们简单介绍一下所涉及的概念。 我们来考虑含一个字节二进制位的寄存器。如果我们将其内容向右移一位,则最右边的位 落到了边界以外,最左端出现了一个空位。对于这个移出的位及留出的空位如何操作是区别各 种移位运箄的特征。一种方法就是将右侧移出的位放置在左端的空位上,这类运算称为 循环移 位 (circular shift 或 rotation )。 因此,如果我们针对一个字节的位模式向右进行8次循环移位,则 所得位模式与初始形式相同。 另外一种方法就是丢弃移出边界的位,并用0填充空位,这类运算称为逻 辑移位 (logical shift )。 这种向左的移位可以实现2乘以二进制补码的运算。因为,二进制数字左移相当于乘2, 而对一个十进制数字左移就相当于乘10。此外,除2运算可以通过二进制位右移来完成。无论对 于哪种移位,在使用某种记数法系统时都一定要小心地保留符号位。因此,右移时留出的空位 (符号位位置)总是用它原来的值来填。保留符号位不变的移位称为算 术移位 (arithmetic shift ). 在各种可能的移位和循环移位指令中,附录 C 描述的机器语言仅包括一条右循环移位指 令,操作码为 A 。 在这个移位运算中,操作数的第一个十六进制数字指定了要循环移位的寄存 器,操作数的其余部位规定要循环移位的位数。因此,指令 A 501 意为“将寄存器5的内容循环 右移1位”。特别地,如果寄存器5最初包含位模式65 (十六进制),那么执行此指令(见图 2-12) 后将包含 B 2。 (尝试一下如何组合使用附录 C 描述的机器语言提供的指令来产生其他移位及循 环移位指令。例如,因为一个寄存器的长度为8位,所以循环右移3位与循环左移5位所得结果 —样 o ) 图 2- 12将位模式65 (十六进制)循环右移1位 66 第 2 章数据操控 2.4.3 算术运算 尽管我们己经提到过算术运算——加、减、乘和除,但还需铙舌几句。首先,我们知道减 法运算可以通过加法及取负来模拟。此外,乘法只不过就是反复进行加法运算,除法就是反复 进行减法运算。 (6 可以减去3个2,所以6除以2为3。)因此,一些小型 CPU 都只装有加法指令或 者加法和减法指令。 我们还应该注意到,每种算术运算都有许多变体。对于附录 C 描述的机器语言里使用的加 法运算,我们己经暗示了这一点。例如,对于加法运算,如果相加的数值用二进制补码记数法存 储,此加法过程的实现就 一定是 每列数字直接相加。然而,如果操作数用浮点记数法存储,加法 过程则为读取每个操作数的尾数,根据指数字段将其向左或右移位,检查符号位,执行加法,最 后将其结果翻译成浮点记数法。因此,尽管它们都是加法运算,但是计算机的实现过程并不相同。 问题与练习 L 堯成指定的运算 w 致: ro : o € o .. oi :^ …; : : : : . ::. m : ' ■■■.: . .;:. :: 1 . : : ' !: '" ;: . :: AND 10101011 AND 1110110 0 AMD OQMllQl : d .: oiooioii e , loogooii f . liiimi :: OR - OR . 111:01100 OR . 001 G . ll 01 ■ ! - . . . . • . •• " "- •- 1 I . T • . . • I . I : : . . , , ! :. .||. i: ■ :. ::■::::. : :- '■ -, : 丨 ■ 1 1 ■ ":. : - '■ 1 i- ... :: ; ; . • ; . . : ... .... I .... :: • : : • ; .. ' g . 01001011 : h . 10000011 i . 11 X 11111 XOR 10101011 XOR 11101100 XQR 001011 Q 1 2. 假如想从一个字节中分离出中间的 4 偉:将其他4个位设为0,却不千扰中间的4位。请问必须使用什 ; 3.' 如想对一个字节的中向4位取 反码, :忒他4位保_妾。彳请 N 必细使用么掩齒及什么运算? 4 . adS 如想对一个位串的前2位进行 XQRdg 算,然后以下列方式继续 下去: 把上次计算的结果与位串的 : 下一个位进行 XORig 算。请问最后的计算结果与该串中1的个数有什么关系? 对二文锔码柯,需翠确定使用什么样的奇偶校验位 ,、 问题 a 与此问题有廿么联系? 是植方便的。例如,逻辑运算 AND 将两个位组合的方法同乘法运算 一祥。哪一种逻辑运算和两个位的力 ff 法几乎相同,这样情况¥会导致什么问题? 6. 将 ASCII 码中的小写字母变为犬写字母,请问需要使用什么逻辑运算和什么掩码?大写字母变小写时 7. 列桉串中,完遠德赤右_ 位会得 ® 什么结果 t a . 01101010 b . 00001111 c . 01111111 8. 对于下面用十六进制表示的字节,执行循钚左移1位的运算,结果是什么?请用十六进制形式给出 限酌 0 1 :'■:: . ,i I ' , ... • i- ' I I .... I , , ■ ■ "; ■; , ': I : : Ml .. -i II - . :r i : f .. 江:: 5 ! C :...... c .; B 7 d .35 9. —^8 位位串循环右移 3 取等价宁循环左移多少位? 10. 如果位模式 OUOIOIO 与11001100是以二进制补码记数法表示的值,请问它们的和的位模式是什么? : , 如果这_个镡模式是用華1_讨论的浮点格式表示的,那么结果又是什么? ::: n : :勧有 fft 录 cfe 述軔机写 程章: 赁将;为 A 7 的存储单元的最高有效位设为1 , 其他位的值… 12-使用附录 C 描述的机器语言编写一十_序: 宕将 存储单_的中间4位复制到存储单茺 E 1 中的最低4 ; :fe : ,:, r :.-. :;:: : .:^;.-^ i : : :::. '■ |. !:. : ::; :! ! i ; -. ■>^ : : : ; ;. ■ :■:: !:: :::■::,;; ; V :. : I ,: ■:;; ; ;.. . : : ;;.::::Vi : ' :: ;:::■ :' i ; M ■ :: :,:, ' *2.5 与其他设备通信 67 *2.5 ■% 其他设备通信 主存储器和 CPU 构成了计算机的核心。本节将研究这个核心(将称它为计算机)如何与外 围设备通信,如海量存储系统、打印机、键盘、鼠标、监视器、数码相机以及其他计算机。 2.5.1 控制器的作用 计算机与其他设备的通信通常是通过称为控制器 ( controller ) 的中间设备来处理的。对于 个人计算机,控制器可能由永久安装在其主板上的电路组成,或者为了灵活,它会采用电路板 的形式插入主板的插槽中。无论哪种形式,控制器都是通过电缆与计算机箱里的外围设备相连 接的,或者与计算机背面称为端口 ( port ) 的连接器相连接,其他外围设备可以插到这些端口 上。这些控制器有时候本身就是小型计算机,每个都有自己的存储电路和简单的 CPU , 可以实 现指挥该控制器活动的程序。 控制器将信息和数据来回地在两种形式之间 转换: 一种是与计算机内部特征相适应的形式, 另外一种是与所连接外围设备相符的形式。最初,每个控制器都是为特定类型的设备设计的。 因此,购买一种新的外围设备常常也就需要同时购买一个新控制器。 最近,人们已经开始在个人电脑领域开发标准,例如 USB (Universal Serial Bus , 通用串行 总线)和 FireWire (火线),这样一个控制器就可以处理多种设备。例如,一个 USB 控制器可以 用作计算机与其他任何同 USB 兼容的系列设备的接口。现在,市场上可以与 USB 控制器信的 设备包括鼠标、打印机、扫描仪、海量存储设备、数码相机,以及智能手机。 每一个控制器通过连接到相同的总线(该总线用来连接计算机的 CPU 和主存)完成与计算 机的通信(图2-13)。由于这种连接,每个控制器都能够监控 CPU 与主存储器之间正在发送的信 号,也可以将自己的信号插入总线。 CD 驱动器调制解 控制器 控制器 : - ' ■- - ■ _ 广 ,〆、 总线 ' : ~ J - — J : I - ■- ■ 二 - •• ^ _-二:__-「•■_: . ~y. - 1 . ’《 j,=r _二_ 1 ; 厂 '■■■■■■■, • : = : = :=: = ,乂,广:二: ‘ . . ' .. ■ 1 1 '. -- ^圓 - __ __ 圓 _ — ■- 二二二 _ __ — ,^, r :二:•- --••—••• —,, ■二二=二三/ :,丰赛 - ■ . ■ . J .\ / ''-- :二:一: - T 二 _ 乂 - W = 「' :■ “,- -- 器、、 搜制器 监视器 磁盘驱 动器’ 图 2-13 连接到计算机总线的控制器 通过这种安排, CPU 能够以与主存储器通信的方式与连接在总线上的控制器通信。为了发 送一个位模式给控制器,该位模式首先要在 CPU 的一个通用寄存器中构建,然后由 CPU 执行一 个类似 STORE 指令的指令,将该位模式“存储”到控制器中。类似地,当要从一个控制器接收 一 个位模式时,要使用一条类似 LOAD 指令的指令。 68 第 2 章数据操控 - ■ * ■- -_ Ml ■■— 玲翻 AM USB ( 通用串行总线)和 FiteWire ( 火线)是标准化的串行通信系紙,它们简化了给个 人电脑添加」外围设备的过程。 USB 由荚特冰公司主导研发, FireWire 则由苹杲公司主导研发。 两者的目的都是適过一个控制器提供外 部鴂口 ,并用该端〒来连接各种外 1 围设备。在该设置 中,控制番将计算机内部信号特征转换成相应的 USB 或 tireWire 标准信号,反之,为了与控 制器通信,与控制器相连接的每个设备都将其内部特性转换成相同的 U 萌或 FireWire 标准。 于是,给 PC 添加新设备就不再需要增加新的控制器,只需要在 USB 端口或 FireWire 端口插入 分别与其兼容的设^ 对比而言, FireWire 的传输速率更高,但是 USB 技术成本低,因此姜俾成本大众市场领 域占据突出地位。现在,市场上兼容 USEf ^ 设备有鼠标、键盘、打印机、 趣描 仪、数码相机、 智能手机,以及为备份应用设计的海量存储系统。 FireWire 应用趋向于_在需要更高传输 速率的设备上,例如摄像机和联机海量存搪系统。 在某些计算机的设计中,通过控制器的数据传输(输入与输出)直接使用 LOAD 和 STORE 操作码(虽然这些操作码已经用于同主存储器的通信)。在这种情况下,每个控制器都被设计为 响应唯 一一 组地址的引用,而主存储器被设计成忽略对这些地址的引用。因此,当 CPU 在总线 上发送一条消息,要把一个位模式存储到一个分配给某个控制器的存储器地址时,这个位模式 实际上是存储到该控制器中,而不是主存储器中。同理,如果 CPU 试图从这样的存储器地址接 收数据(如 LOAD 指令),那么它所接收到的位模式将来自控制器而不是存储器。这样的通信系 统称为 存储映射输入/输出 ( memory-mapped 1/0), 因为该计算机的输入/输出设备好像是在各种 存储器位置里(图2-14)。 CPU 总线 主存 储器 控制器 一 外围设备 图 2-14 存储映射输入/输出的概念表示 另外一种存储器映射输入/输出的方法是在机器语言中提供特定的操作码,用以规定通过控 制器的数据传输(输入与输出)。具有这些操作码的指令称为 I / O 指令。例如,如果附录 C 中描述 的机器语言遵循这种方法,它可能包括诸如 F 5 A 3 这样的一条指令, F 5 A 3 指的是“将寄存器5的 内容存储在由位模式 A 3 指定的控制器中”。 2.5.2 直接内存存取 因为控制器是连接到一台计算机的总线上的,所以它就有可能在 CPU 不使用总线的几纳秒 时间里实现与主存储器的通信。控制器这种存取主存储器的能力称为 DMA (Direct Memory Access , 直接存储器存取),可以极大地提高计算机的性能。例如,要从磁盘的一个扇区读取数 据, CTU 可以将编码为位模式的请求发送给连接这个磁盘的控制器,要求该控制器读取这个扇 区并将数据存储在指定的一块主存储器区域中。在该控制器执行此读操作并通过 DMA 将数据存 储在主存储器时, CPU 可以继续执行其他任务。于是,这两个活动会被同时执行。 CPU 将执行 某个程序,而控制器则监视磁盘与主存储器之间的数据传输。这样,在相对缓慢的数据传输过 程中, CPU 的计算资源就不会被 浪费。 使用 DMA 同样也有不利影响,它使计算机总线的通信复杂化。位模式必须在 CPU 与主存储 *2.5 与其他设备通信 69 器之间、 CPU 与每个控制器之间,以及每个控制器与主存储器之间进行传送。协调总线上的所 有这些活动是个很大的设计难题。即使设计非常出色, CPU 与控制器竞争总线存取时,中央总 线也可能成为障碍。此障碍称为冯 •诺依曼瓶颈 (von Neumann bottleneck ) ,因为它源于冯 •诺 依曼体系结构 (von Neumann architecture ) ,在该结构中, CPU 是通过中央总线从主存储器 取指的。 2.5.3 握手 两个计算机部件之间的数据传输很少是单向进行的。即使我们可以把打印机看做是接收数据 的设备,但事实上它也向计算机发送数据。毕竟,计算机产生字符并向打印机发送字符的速度要 远远快于打印机能够打印的速度。如果计算机盲目地把数据发送给打印机,那么打印机很快就落 在后面了,结果是使数据丢失。因此,诸如打印文件这样的过程都会包括持续的双向对话,计算 机和外围设备之间交换设备状态的信息,协调它们之间的活动。这个过程称 为握手 ( handshaking )。 握手通常涉及一个 状态字 (status word ) ,它是由外围设备生成并发送给控制器的一个位模 式。该状态字是一个位图,其中的各个二进制位反映了该设备的各种状态。以打印机为例,其 状态字的最低有效位数值可以表示该打印机是否缺纸,而下一个位可以表示该打印机是否已经 准备好再接收数据,另外还有一位可用于指出是否卡纸。控制器是自己响应这些状态信息,还 是交由 CPU 来处理,这取决于不同的系统。无论哪种情况,状态字都提供了一种机制,用于协 调与外围设备的通信。 2.5.4 流行的通信媒介 计算设备之间的通信由两种途径 处理: 并行及串行。这些术语指的是传输信号的方式 。并 行通信 (parallel communication ) 指的是若干信号同时传输,每个信号都在各自的“线路”上。 这种技术数据传输快,但是需要相对复杂的通信通路。例如计算机内部总线,其中多条线路被 用于同时传输大量数据块及其他信号。 与此相反, 串行通信 (serial communication ) 指在一条信号线上一个信号接一个信号地传 输。相对于并行通信,串行通信只需要一条相对简单的数据路径,这也是它很流行的原因。 USB 与 FireWire 在短短几米的距离内提供相对高速的传输速率,属于串行通信。对于相对较长的 距离(在家中或者办公楼里),通过以太网连接(见 4.1 节)的串行通信,无论是通过电线还是 无线电广播连接,都很流行。 多年来,传统的语音电话线在远距离通信方面一直主宰着个人电脑领域。这些通信路径都只 有一根电线并通过它逐一传输语音信号,本质上属于串行系统。这样的数字数据传输实现过程 如下:首先利 用调制解调器 ( modulator - demodulator , 缩写为 modem ) 将位模式转换为听得见的 音调,并通过电话系统串行传输,然后在目的地由另一个调制解调器将这些音调重新转换成二进 制位。 为了通过传统的电话线实现更加快速的远距离通信,电话公司提供了一种称为 DSL (Digital Subscriber Line , 数字用户线路)的服务,它利用以下 事实: 现有的电话线实际上能够处理比传 统话音通信所用频段更宽的频率范围。确切地说, DSL 使用高于可听范围的频率传输数字数据, 将较低频谱用于语音通信。虽然 DSL 已非常成功,但电话公司正迅速升级自己的系统来使用光 纤线路(光纤线路比传统电话线路更容易支持数字通信)。 其他可与 DSL 和光纤相竞争的技术包括用于有线电视系统的电缆以及通过高频无线电广播 的卫星链路 D 70 第 2 章数据操控 2.5.5 通信速率 一个计算部件与另外一个计算部件之间传输数据位的速率是以 bit/s (bit per second , 比特/ 秒)计量的。常用的单位有 Kbit/s ( kilo - bit / s , 等于 10 3 bit / s )、 Mbit/s ( mega - bps , 等于 10 6 bit / s ) 和 Gbit/s ( giga - bps , 等于10 9 bit / s )。(注意位与字节之间的 区别: 8 Kbit / s 相当于1 KB / s )。 使用 缩写形式时,小写的英文字母 b 通常表示位 ( bit ), 而大写的英文字母 B 通常表示字节 ( byte)o 对于短距离通信, USB 及 Fire Wire 可以提供几百 Mbit / s 的传输速率,对于大多数多媒体应用 已经足够了。再加上便利性及相对低价,如今它们已被广泛用于家用计算机与本地外围设备的 通信,如与打印机、外部硬盘驱动器以及相机的通信。 通过结合 多路复用技术 ( multiplexing , 数据编码或混合,使得一条通信路径可完成多条通 信路径的功能)及数据压缩技术,传统的语音电话系统能够支持 57.6 Kbit / s 的传输速率,这无法 满足当今多媒体和因特网应用(如 YouTube 和 Facebook ) 的需要。播放 MP 3 音乐需要大约64 Kbit/s 的传输速率,即使播放低品质的视频也需要用 Mbit / s 计量的传输速率。这正是能够提供 Mbit / s 范 围的传输速率的 DSL 、 电缆以及卫星链路等取代传统音频电话系统的原因。(例如, DSL 提供的 传输率大约为54 Mbit / s 。 ) 一个特定设置可获得的最大速率,取决于通信路径的种类以及实现过程中使用的技术。这 个最大速率通常大致等同于通信路径的带宽 ( bandwidth ), 但该术语除了传输速率还有容量的 含义。也就是说,说一条通信路径具有高带宽(或提供宽带服务)意味着一条通信路径能以高 速率传输位,同时意味着该通信路径还能够同时携带大量信息。 |'1 : ‘②篆 _ :漏議鎌 帑:::細附:纏逯#融1_誠齡雜 .. .儀鸯送给奪( "■ :' V -'/ ::: 、’.:臺 ' : d .. @细果_器7包含#母^^Hi 姆讓节 桌磁 葡 打算遍磁盘■动器中 g 从操转錄盘电读到的位么磁巍 tea 海 会-盘 盔之:_逋信 大约是多少? . : :乂 : ; .以 : -:^y :: . 3. 本以 编鉍的 细0宽承说, g 敏 MMbi 的的速传輪 爹久 ? t 〔 、::、: *2.6 其他体系结构 . . . — …. . 为了拓宽视角,我们来考虑一些已经讨论过的传统计算机体系结构的替代方案 D 2.6.1 流水线 电子脉冲在电线上的传播要比光速慢。光大约每纳秒 ( ns , 十亿分之一秒)能传播1英尺的 距离,然而 CPU 中的控制单元至少需要2 ns 才能从1英尺之外的存储单元中读取到指令。(必须发 送读请求到存储器,这至少需要 1 ns , 而指令又必须送回 CPU , 这至少也需要 Ins 。) 因此,在这 样的机器中取指和执行一条指令需要若干纳秒——这就意味着提高计算机执行速度的问题最终 将变成小型化问题。 *2.6 其他体系结构 71 然而,提高执行速度并不是改进计算机性能的唯一途径。真正目的是改进机器的吞吐量 (throughput) -机器在给定时间内可以完成的工作总量。 在不要求提高执行速度的前提下,增加计算机吞吐量的一个例子涉及流水线技术 (pipelining), 该技术允许 一 个机器周期内各步骤重叠进彳丁。特别是,当执彳丁一■条指令时,可以 取下一条指令,这也就意味着在任何一个时刻可以有不止一条指令在“流水线”上,每条指令 处在不同的处理阶段。这样,尽管读取和执行每条指令的时间保持不变,计算机的总吞吐量却 提高了。(当然,当到达一条 JUMP (转移)指令时,不会实现预取指令来提高效率的效果,因 为“流水线”上的指令不是所需要的。) 现代计算机设计已使得流水线思想大大超越了我们所举的例子。它们经常能够同时读取若 干条指令,并且一次可以执行多条彼此互不依赖的指令。 . 科技进步使得可以放置越来越多的电路在一个硅片上,,以致计算机部件之:间的物鋰差别 逐辦变小。 例如' 单个®丼就可外每括和主存储畢#屬是片占摹统本象玲 祷是在单 f 设备+提挺^免整蜣奉滅 〆 :掏參减♦在克高的 ㈤ 1 &釋崩 在其他情况下,单个设备中还包含相同电路的多个复制品。:,它最初的形成^ 包士若 干独;立门 或者多重触发器的芯片.在今天的技米条件下,单个芯片可以存放不伞^个完整的这 就是多核 CPU 设备的基础体系 结构: 在同一芯片上存在踌个或更多 CPU 以及共用的高速拿冲存 储器。 (& 含两个处理单秀的多核 CPU 通常被称作双桂 CPU 。) 这种 i 更審简;% ^ fMIMD 系蘇_ 建,并 S 板迅速 应用于 家用计算机。:___ 一: ’ : / - 2.6.2 多处理器计算机 可以把流水线技术看做迈向并行处理技术 (parallel processing) 的第一步,并行处理技术是 若千活动在同一时间里实现的性能。然而,真正的并行处理技术需要多个处理单元,于是产生 了多处理器计算机。 当今许多计算机的设计都基于这种思想,其中一个策略是将若干处理单元都连接到同一个 主存储器上,其中每一个都像单处理器机器中的 CPU 。 在这样的配置下,各处理器可以独立地 工作,并通过把相关的信息放在公共存储单元里来协调各自的工作。例如,当某个处理器遇到 一 个大任务时,它可以将部分任务的程序存储在这个公共存储器中,然后求其他处理器执行 它。结果产生这样的计 算机: 不同的指令序列在不同的数据集上操作,相对于较传统的 SISD ( Single-Instruction stream , Single-Data stream , 单指令流单数据流)体系结构,它称为 MIMD ( Multiple-Instruction stream , Multiple-Data stream , 多指令流多数据流)体系结构。 多处理器体系结构的一个变体是将多个处理器链接起来,使得它们一起执行同一个指令序 列,每个处理器都有各自的数据集。这就产生了 SIMD ( Single-Instruction stream , Multiple-Data stream , 单指令流多数据流)体系结构。这种计算机适用于这样的 应用: 在一大堆数据中,对 于其中每组类似的数据项都要执行同样的任务。 并行处理的另外一种方法是将许多小型机器聚集成为大的计算机,每台机器都有自己的存 储器和 CPU 。 这样,每台小型机器都与它相邻的一台或几台机器相连接,使得赋予整个系统的 任务可以分割到各台小机器上实现。因此,如果分配给某台内部机器的一项任务可以分割为若 干独立的子任务,那么这台内部机器可以请求它的邻居们并发地完成各个子任务。这样,完成 任务的时间可以比由一台单处理器计算机独立完成任务所需要的时间少得多。 72 第 2 章数据操控 ,2 V 截會薦水論计筹姑土淨 fe ,:3 节间琿•如 擊勝:肩 : L 撝有两个,“中央 w 雖理 垂逄 fe 到爾心♦存储_表,但执待不- 的輕, :坪假鞋“其增十个,舞赛 ,:;嘗_嗜存鳍单元辑晦,个需襄繪 醉: :: >时又如何呢? 7. 下面是用附录 C 描述的机器语言编写的指令。 把它翻译成为自然语言。 a . 7123 b . 40 E 1 c . A 304 d . B 100 e . 2 BCD 8 . 假设有一机器语言,指令的操作码字段为 4 位。 那么该语言可以有多少条不同的指令?如果 操作码字段增加到8位呢? 9. 将下列指令由自然语言翻译为附录 C 描述的 机器语言。 a . 将十六进制值 77 装入 ( LOAD ) 寄存器 6。 b . 将存储单元77的内容装入寄存器7。 c . 如果寄存器0的内容与寄存器 A 的值相同, 则转移 ( JUMP ) 到存储器地址为24的 指令。 d . 将寄存器4循环右移 ( ROTATE ) 4位。 e . 将寄存器 E 和2的内容进行 AND 运算,结果 存于寄存器1。 10. 重写图 2- 7 中的程序,假定相加的数值用浮点 记数法编码,而不是二进制补码记数法。 11. 下面是用附录 C 描述的机器语言编写的指令。 请按照它们的执行是否改变存储地址为 3 B 的 存储单元的值、是否读取地址为 3 C 的存储单 元的内容、是否与地址为 3 C 的存储单元的内 容没有关系进行分类。 a . 353 C b . 253 C c . 153 C d . 3 C 3 C e . 403 C 12. 假设附录 C 描述的机器里,从地址 00 到 03 的存 储单元中包含下列位模式。 复习题 73 (续) 地址 内容 地址 内容 00 26 06 3A 01 55 07 00 02 C0 08 C0 03 00 09 00 a . 将第一条指令翻译为自然语言。 b . 如果机器在程序计数器的值为00时启动, 那么机器停止时寄存器6中是什么位模式? 13- 假设附录 C 描述的机器里,从地址00到02的存 储单元中包含下列位模式。 地址 内容 00 12 01 21 02 34 a - 如果机器在程序计数器值为00时启动,执 行的第一条指令会是什么? b . 如果机器在程序计数器值为01时启动,执 行的第一条指令会是什么? 14. 假设在附录 C 描述的机器里,从地址 00 到 05 的 存储单元中包含下面的位模式。 地址 内容 00 12 01 02 02 32 03 42 04 C0 05 00 假设机器在程序计数器的值为00时启动,回答 下面的问题。 a . 将要执行的指令翻译成自然语言。 b . 当 机器停 止时,地址为42的存储单兀中是 什么位模式? c . 当机器停止时,程序计数器中是什么位 模式? 15:假设在附录 C 描述的机器里,从地址00到09的 存储单元中包含下列位模式。 地址 内容 00 1C 假设机器在程序计数器的值为00时启动,回答 下列问题。 a . 当机器停止时,地址为00的存储单元里有 什么? b . 当机器停止时,程序计数器中会是什么位 模式? 16. 假设在附录 C 描述的机器里,从地址 00 到 07 的存储单元中包含下列位模式。 地址 内容 00 2B 01 07 02 3B 03 06 04 C0 05 00 06 00 07 23 a . 假设机器在程序计数器的值为00时启动, 请列出包含待执行程序的存储单元的地址。 b . 列出用于存储数据的存储单元的地址。 17. 假设在附录 C 描述的机器里,从地址 00 到 0 D 的 存储单元中包含下列位模式。 地址 内容 00 20 01 04 02 21 03 01 04 40 05 12 06 51 07 12 08 BI 09 0C 0A B0 0B 06 0C C0 0D 00 01 03 02 2B 03 03 04 5A 05 BC 假设机器在程序计数器的值为00时启动。 74 数据操控 a. 当机器停止时,寄存器0中是什么位模式? b . 当机器停止时,寄存器1中是什么位模式? c. 当机器停止时,程序计数器中是什么位 模式? 18. 假设在附录 C 描述的机器里,从地址 F 0 到 FD 的存储单元中包含下列(十六进制)位模式。 地址 内容 F0 20 F1 00 F2 22 F3 01 F4 23 F5 04 F6 B3 F7 FC F8 50 F9 02 FA B0 FB F6 FC C0 FD 00 如果机器在程序计数器的值为 F 0 时启动,那 么当计算机最终执行到地址为 FC 的停机指令 时,寄存器0中的值是什么? 19. 如果在附录 C 描述的机器每微秒(百万分之一 秒)执行一条指令,那么完成问题 18 中的程序 需用时多少? 20. 假设在附录 Cffi 述的机器里,从地址 20〜28 的 存储单元中包含下列位模式。 地址 内容 20 12 21 20 22 32 23 30 24 B0 25 21 26 24 27 C0 28 00 假设机器在程序计数器的值为20时启动。 a . 当机器停止时,寄存器0、1和2中是什么位 模式? b . 当机器停止时,地址为30的存储单元中是 什么位模式? c . 当机器停止时,地址为 B 0 的存储单元中是 什么位模式? 21. 假设在附录 C 描述的机器里,从地址 AF 到 B 1 的存储单元中包含下列位模式。 地址 内容 AF B0 B0 B0 BI AF 如果机器在程序计数器的值为 AF 时启动,那 么会发生什么? 22. 假设在附录 C 描述的机器里,从地址 00 到 05 的 存储单元中包含下列(十六进制)位模式。 地址 内容 00 25 01 B0 02 35 03 04 04 C0 05 00 如果机器在程序计数器的值为00时启动,那么 机器在什么时候会停止? 23. 对于下面每种情况,用附录 C 描述的机器语言 编写一个小程序来完成以下任务。假定每个程 序都放在从地址00开始的存储器里。 a . 将存储单元 D 8 的值移动到存储单元 B 3。 b . 交换存储单元 D 8 和 B 3 中的值。 c . 如果存储单元44的值是00,则将值01存放 在存储单元46中:否则,将值 FF 存放在存 储单元46中 。 24. 在计算机爱好者中曾经流行一种叫磁芯大战 (Core Wars ) 的游戏 ■ -战舰游戏的变体。 (术语磁芯来源于早期的一种存储_术,它用 磁材料的小环的磁场方向表示0和1。小环称 为磁芯。)这个游戏是在两个对立的程序之 间玩的,每个程序分别存储在同一台计算机 的存储器的不同位置里。假设该计算机轮流 执行这两个程序,先执行一个程序的一条指 令,再执行另一个程序的一条指令。每个程 序的目标是通过把额外数据写到另外一个程 序上来破坏对立 程序; 不过,哪个程序都不 知道对方的位置。 a . 用附录 C 描述的机器语言编写一个程序,采 用防卫的方式,以最小的代价玩此游戏。 复习题 75 b . 用附录 C 描述的机器语言编写一个程序, 通过不断变动自己的位置来避免受到对立 程序的袭击。更确切地说,在位置00开始 编写程序,使其把自己复制到位置70,再 转移到这个新位置。 c. 扩展 ( b ) 中的程序,继续将新位置的程序重 新定位。具体来说,先将程序移至位置70, 然后移到 E 0 070+70),再移到60 070+ 70+70),等等。 25. 用附录 C 描述的机器语言编写一个程序,用它 计算存放在存储单元 AO 、 Al 、 A 2、 A 3 中的浮 点数的和,并将结果存入存储单元 A 4 中。 26. 假设在附录 C 描述的机器里,从地址00到05的 存储单元中包含下列(十六进制)位模式。 地址 内容 00 20 01 C0 02 30 03 04 04 00 05 00 如果机器在程序计数器的值为00时启动,会发 生什么? 27. 假设在附录 C 描述的机器里,地址为08和09的 存储单元中分别包含位模式 B 0 和08,并且机 器启动时程序计数器中包含值08,那么会发生 什么? 28. 假设下列用附录 C 中描述的机器语言编写的 程序存储在从地址30 (十六进制)开始的主存 储器中。当执行该程序时它会完成什么任务? 2003 2101 2200 2310 1400 3410 5221 5331 3239 333B B24 8 B038 C000 29. 概述当附录 C 描述的机器执行一条操作码为 B 的指令时所涉及的步骤。用一组说明来表示你 的答案,就像你在告沂 CPU 做什么。 *30. 概述当附录 C 描述的机器执行一条操作码为5 的指令时所涉及的步骤。用一组说明来表示你 的答案,就像你在告诉 CPU 做什么。 *31. 概述当附录 C 描述的机器执行一条操作码为6 的指令时所涉及的步骤。用一组说明来表示你 的答案,就像你在告诉 CPU 做什么。 *32. 假设在附录 C 描述的机器里,寄存器4和5中分 别包括位模式 3 A 和 C 8, 在执行下列每条指令 后,寄存器0中会留下什么位模式? a. 5045 b . 6045 c. 7045 d . 8045 e_ 9045 *33 .利用附录 C 描述的机器语言,为完成下面每个 任务分别编写一个程序。 a. 将存储单元44中存储的位模式复制到存储 单元 AA 中。 b . 将存储单元34中的最低4个有效位变成0, 并保持其他位不变 。 c . 将存储单元 A 5 中的最低4个有效位复制到 存储单元 A 6 中的最低4个有效位,并保持 A 6 中的其他位不变。 d . 将存储单元 A 5 中的最低4个有效位复制到 存储单元 A 5 中的最高4个有效位。(于是, A 5 中的前4位将和后4位相同。) *34. 完成下列运算。 a. 111001 b. 000101 AND 101001 AND 101010 C, 001110 d. 111011 AND 010101 AND 110111 e. 111001 f. 010100 OR 101001 OR 101010 g- 000100 h. 101010 OR oioiai OR 110101 i. 111001 j- 000111 XOR 101001 XOR 101010 k. 010000 1 . min XOR 010101 XOR 110101 *35. 为了完成下面的任务,确定所需要的掩码和逻 辑运算。 76 第 2 章数据操控 a . 将一个8位位模式的高4位置0,并且不影响 其他位。 b . 将一个8位的位模式最高有效位取反,并且 不影响其他位。 c . 将一个8位的位模式取反。 d . 将一个8位位模式的最低有效位置0。并且 不影响其他位。 e . 将一个8位位模式的除最高有效位外所有 的位都置1,并且不改变最高有效位。 *36. 确定一个逻辑运算(以及相应的掩码),使得 当其用于一个8位的输入串时,当且仅当输入 串为10000001时,产生的输出为全0位串。 *37. 描述一组逻辑运算(以及它们相应的掩码), 使得当其用于一个8位的输入串时,当这个输 入串的最高位和最低位都是1时输出结果为全 0 位串; 否则输出中至少应包含一个1。 *38 •对下列位模式执行循环左移 4 位后,结果 如何? a . 10101 b . 11110000 c . 001 d . 101000 e . 00001 *39. 下列字节用十六进制记数法表示,对其执行循 环右移2位后,结果如何?(用十六进制记数 法写出答 案。) a . 3 F b . 0 D c . FF d . 77 *40. a . 在附录 C 描述的机器语言中,用什么样的单 个指令可以完成寄存器 B 循环右移5位? b . 在附录 C 描述的机器语言中,用什么样的单 个指令可以完成寄存器 B 循环左移2位? *41. 用附录 C 描述的机器语言编写程序:把地址为 8 C 的存储单元的内容颠倒过来。(也就是说, 对于地址 8 C 最后的位模式,从左向右读取与 最初从右向左读取一致。) *42 •用附录 C 描述的机器语言编写 程序: 将地址 A 2 中存储的值减去 A 1 中存储的值,并将结果 存于地址 A 0 中。假定值用二进制补码记数法 编码。 *43 ■高清视频可以以30龟 s 的速率传输,其中每- 帧的分辨率为 1920 X 1080 像素,每像素使用 24位。这种格式的无压缩视频流可以通过 USB 1_1 串行端口发送吗? USB 2.0 串行端口呢_? USB 3.0 串行端口呢?(注意 , USB 1.1 、 USB 2.0 .USB 3.0 串行端口的最大速度分别是 12 Mbit / s 、480 Mbit / s 和5 Gbit / s 。 ) *44. 假设某人在键盘上每分钟能打 40 个单词。(假 设一个单词以 5 个字符计。)如果计算机每微 秒(百万分之一秒)执行 500 条指令,那么该 计算机在此人打两个连续的字符之间可以执 行多少条指令? *45. 对于一个每分钟打40个单词的打字员,键盘每 秒传输多少位才能跟得上?(假定每个字符以 ASCII 编码,每个单词以 6 个字符计。) *46. 假设附录 C 中描述的机器与使用存储映射输 入/输出技术的打印机通信,同时假设地址 FF 用于将字符发送给打印机,地址 FE 用于接收 该打印机的状态信息。特别地,假设地址 FE 的最低有效位用于指示该打印机是否准备好 接收下一个字符 (0 表示“未准备好”,1表示 “准备好”)。从地址00开始,编写一个机器 语言例程,它等待打印机准备好接收下一个字 符,然后把由寄存器5中位模式表示的字符发 送给打印机。 *47. 用附录 C 描述的机器语言编写一个程序:它在 地址从 A 0 到 C 0 的所有存储单元中存放0,但是 它应该足够小以致能够存放在地址从00到13 (十六进制)的存储单元中。 *48 ■假设某计算机硬盘上有200 GB 存储空间可 用,以15 Mbit / s 的速率从宽带上接收数据。 以这个速率,需要多久可以存满可用的存储 空间? *49. 假设某卫星系统正以 250 Kbit/s 的速率接收串 行数据流。如果一个突发的大气干扰持续了 6.96 s , 那么有多少数据位会受到影响? *50. 假设给你 32 个处理器,每个处理器在一秒钟内 能够完成两个多位数字加法运算100万次。描 述如何使用并行处理技术,使得能够在 6 xl ( T 6 s 的时间内完成 64 个数的求和^单独一 个处理器完成相同的计算需要多少时间? *51. 概述 CISC 体系结构和 RISC 体系结构之间的 E 别。 *52_说出两种提高吞吐量的方法^ *53. 对于计算一组数值的平均值,说明在-台多处 理器的计算机上为何比在一台单处理器的计 算机上快得多? 下面的问题有助于分析一些与计算领域相关的伦理、社会和法律问题。回答这些问题不是 唯一的目的,还应该考虑为什么这样回答,以及你的判断是否对每个问题都标准如一。 1. 假设某计算机生产商开发了一种新型的计算机体系结构。该公司在多大程度上可以拥有 该体系结构所有权?什么样的政策对社会最好? 2. 从某种意义上说,1923年是现今被许多人称为有计划淘汰现象诞生的时间。这一年,由 斯隆领导的通用汽车公司将汽车工业引向了型号概念的年代。其思想是通过改变风格, 而不是必须推出更好的车来提高销售。引用斯隆的一 句话: “我们希望你们对自己现在 的车不满意,于是你们将会购买新车。”如今,计算机行业在多大程度上使用了这种市 场策略? 3. 我们常常在想,计算机技术如何改变了我们的社会。不过,许多人争辩说,这门技术常 常抑制改变的产生,它试图使老系统继续存在,甚至使其地位更加牢固。例如,如果没 有计算机技术,中央政府在社会中的角色会继续存在吗?如果没有计算机技术,中央集 权在今天能够达到什么程度?如果没有计算机技术,我们在多大程度上会更好或更坏? 4. 如果某人认为自己不需要知道机器的任何内部细节(因为有其他人会建造它,维护它, 并解决可能发生的问题),这种想法合理吗?你的答案会取决于这个机器是计算机、汽 车、核电厂还是烤面包机吗? 5. 假设一家厂商生产了一种计算机芯片,但是后来发现它设计上有一个瑕疵。再假设该生 产商在接下来的生产中修正了瑕疵,但决定掩盖最初有瑕疵这一事实,并且不回收已经 售出的芯片,理 由是: 已经售出的芯片没有一个在该瑕疵会导致严重后果的应用中使用。 有人会因为该生产商的决定受到伤害吗?如果没有人受到伤害,而且该决定避免了该生 产商资金的流失亦或者避免了辞退员工,那么该生产商的决定正确吗? 6. 技术进步有助于治愈心脏病,还是会因导致久坐的生活习惯进而导致心脏病? 7. 很容易想象,由于溢出和截断错误而产生的算术差错可能会导致金融或导航方面的灾 难。对于图像存储系统,由于丢失图像细节(也许在勘察或医疗诊断领域)而产生的错 误会有什么后果? 8. ARM 公司是一家为各种消费性电子设备设计处理器的小型公司。它并不制造任何处理 器,而是将其设计授权给半导体厂商(如高通、三星和德州仪器),这些厂商为生产出 的每个部件支付特许权使用费。这种商业模式将计算机处理器的高研发成本分散到了 整个消费性电子市场。今天,95%以上的移动电话(不仅是智能手机)、40%以上的数 码相机和25%的数字电视都使用 ARM 处理器。此外, ARM 处理器还用于小型笔记本、 MP 3 播放器、游戏控制器、电子书阅读器、导航系统等设备中。鉴于此,你是否认为 该公司是垄断者呢?为什么是?为什么不是?随着消费类设备在今天的社会中扮演着 越来越重要的角色,对于这一知名小型公司的依赖是好事吗?或者,是否会引起人们 的担忧? 课外阅读 ______ Carpinelli, J. D. Computer Systems Organization and Architecture. Boston, MA: Addison-Wesley, 2001. Comer , D. E. Essentials of Computer Architecture. Upper Saddle River, NJ: Prentice-Hall, 2005. Dandamudi, S P. Guide to RISC Processors for Programmers and Engineers. New York: Springer , 2005. 78 数据操控 Furber, S. ARMSystem-on-Chip Architecture, 2nd ed. Boston, MA: Addison Wesley, 2000. Hamacher, V. C., Z. G. Vranesic, and S. G. Zaky. Computer Organization, 5th ed. New York: McGraw-Hill, 2002. Knuth, D. E. The Art of Computer Programming, vol. 1, 3rd ed. Boston, MA: Addison-Wesley, 1998. Murdocca, M. J. and V. P, Heuring. Computer Architecture and Organization: An Integrated Approach, New York: Wiley,2007. Stallings, W. Computer Organization and Architecture, 7th ed. Upper Saddle River, NJ: Prentice-Hall, 2006. Tanenbaum, A. S. Structured Computer Organization, 5th ed. Upper Saddle River, NJ: Prentiee-Hall, 2006. 操作系统 一章,我们将讨论操作系统。操作系统是用来协调计算机的内部活动以及检查计算机 与外部世界通信的软件包。操作系统能将计算机硬件转化为有用的工具,我们的目标 就是要理解操作系统做哪些工作,以及它是如何完成这些工作的。要成为有知识的计算机使用 者,这样的背景是极为重要的。 本_内容 3.1 操作系统的15劣 3.2 操作系统的体系结构 3.3 协调机器的馨€ *3.4 处理进程间的竞争 : :. . . .. 3.5 安全性 复习题 社会问题 课外阅读 • • ^ ■ ■ : 操作系统 (operation system ) 是控制计算机整体运行的软件。它提供了用户可以存储和检 索文件的方法,提供了用户可以请求执行程序的接口,还提供了执行被请求程序所必需的环境。 操作系统最著名的例子是 Windows , 微软公司己经发布了很多版本,并广泛用于 PC 机领域。 另一个被广泛认可的例子是 UNIX , 它是服务于较大的计算机系统和 PC 群的流行选择。事实上, UNK 是其他两个操作系统的 核心 : Mac OS 是苹果公司为其一系列 Mac 机提供的一种操作系统, Solaris 由 Sun Microsystems (现归 Oracle 所有)开发得来。另外,还有能够运用于大型机和小型 机的 Linux 操作系统,该系统最初是由一些计算机爱好者以非盈利的目的开发的,到目前为止, 包括 IBM 公司在内的许多商业机构都发布了 Linux 操作系统。 对于非专业的计算机用户,他们只能感觉到不同操作系统表面上的不同,而对于计算机 专业人员来说,不同的操作系统可能意味着使用完全不同的工具或在传播和维护工作中遵循 完全不同的理念。然而,所有主流操作系统的核心都是解决计算机专家在半个多世纪之前就 已经遇到的那些问题。 3:1 操作系统的历史 今天的操作系统经过长期的演变已经成为大而复杂的软件包。20世纪四五十年代,计算机 不是很灵活,效率也不高。 一 台计算机会占据整个房间。执行程序需要大量的设备准备工作, 如安装磁带、把穿孔卡片放在读卡机上、设置开关等。每个程序的执行称为一个作业 ( job ), 它是作为一个独立的活动处理的——为执行该程序准备好计算机,执行程序,然后在下一个程 序的准备工作开始之前,必须重新获取磁带、穿孔卡片等所有一切。当几个用户需要共享一台 机器时,操作系统提供签名表,以便各个用户能够预订到一段机器时间。在分配给某个用户的 时间段内,机器就完全处于该用户的控制之下。这段时间通常是从程序的准备开始,接下来是 80 第 3 章操作系统 短时间的程序执行过程。一个用户本可以在很短的时间内尽可能多做一件事情(“它仅需一分 钟”),但下一个用户己经迫不及待地要使用机器做准备工作了。 在这样的环境下,操作系统开始作为一个系统致力于简化程序的准备工作,提高作业之间 的过渡效率。操作系统早期的开发是用户与设备的分离,用以避免用户进出计算机机房。为此 雇用了计算机操作员来操作机器。任何人如果需要运行程序,就必须把程序、所需的数据以及 有关程序需求的特别说明提交给操作员,由操作员返回结果。操作员所做的工作就是把这些资 料输入到计算机的海量存储器,然后由称为操作系统的程序从那里一次一个地读入并执行程序。 这就是 批处理 (batch processing ) 的开始 -把若干个要执行的作业收集到一个批次中,然后 执行而无需与用户发生进一步的交互。 在批处理系统中,驻留在海量存储器中的作业在作 业队列 (job queue ) 里等待执行(见图 3-1)。 队列 ( queue ) 是一种存储机构,对象(这里指作业)按照 FIFO ( first - in , first - out , 先进 先出)的方式在队列里排队。也就是说,对象的出列顺序和入列顺序一致。实际上,大多数作 业队列不是严格遵循 FIFO 结构的,主要是因为大多数操作系统都考虑了作业的优先级,结果就 造成了在队列中等待的作业有可能被优先级更高的作业挤掉。 作业:程序、 结果 数据和指令 ♦ 用户域 机器域 图 3-1 批处理 在早期的批处理系统中,每个作业都伴随着一组指令,用来说明为这个特定的作业准备机 器时所需的步骤。这些指令用作业控制语言 ( JCL ) 进行编码,与作业一起存放在作业队列里。 当一个作业被选中执行时,操作系统在打印机上打印出这些指令以便计算机操作员阅读和遵照 执行。在今天,计算机操作员与操作系统之间的通信仍然存在,如报告“磁盘驱动不可访问” 和“打印机没有响应”之类的错误的 PC 操作系统。 在计算机和用户之间,用计算机操作员作为媒介的最大缺 点是: 作业一旦提交给操作员, 用户就与它无法交互。这种方法对于某些应用是可以接受的,如工资表的处理,因为在这里, 数据与所有的处理决策事先已经建立了。然而,当在一个程序的执行期间,用户必须与该程序 进行交互时,这种方法就无法让人接受了。例如,在预订系统中,预订和取消操作必须及时报 告; 在字处理系统中,文档是以动态的写入和重写方式开 发的; 在计算机游戏中,与计算机的 交互性是游戏的主要特征。 为了适应这些需求,人们幵发了新的操作系统,它们允许执行一个程序来通过远程终端与 用户对话~ 这种特性称 为交互式处理 (interactive processing ) (见图3-2)。一个终端只不过是 一台电子打字机,通过电子打字机用户能够进行输入并且读出那些打印在纸上的计算机响应。 当今的终端已经演变成称为工作站的设备(这些设备更为精细复杂),而且在必要时甚至还可以 是一台完全独立运行的完整个人电脑^: 成功交互式处理的最重要之处在于,计算机的动作能够足够快速地协调用户的需求,而不 是让用户遵循计算机的时间表。(在进行工资表的处理时,计算机能够根据所需的时间量调度得 很好,但是在使用字处理程序时,如果机器不能敏捷地对字符的打印作出响应,用户会很沮丧。) 3.1 操作系统的历史 81 从某种意义上说,计算机在一个限期内被强制执行任务,这一过程就是众所周知的实 时处理 ( real-time processing ) ,并且动作的完成也是按实时方式发生的。也就是说,要是说计算机以实 时的方式完成一个任务,就意味着计算机完成任务的速度足以跟上该任务所在的外部(现实世 界)环境中的行为。 用户域 :::. i ... 机器域' 图 3-2 程序、数据、指令和结果 交互式处理 如果要求交互式系统一次只服务于一个用户,那么实时处理就不存在问题了。但是在20世 纪六七十年代,计算机比较昂贵,因此每台计算机不得不服务于多个用户。因此,工作在终端 的若干个用户在同一时间寻求一台机器的交互式服务,并且对实时的要求出现障碍就不足为奇 了。如果操作系统对于多用户环境仍然坚持一次执行一个作业,那么将只有一个用户可接受到 满意的实时服务。 针对这个问题的解决方案就是设计能同时给多个用户提供服务的操作系统,这一特点称为 分时 ( time - sharing )。 实现分时的一种方法就是应用称为 多道程序设计 ( multiprogramming ) 的 技术,其中时间被分割成时间片,每个作业的执行被限制为每次仅一个时间片。在每个时间片 结束时,当前的作业暂时放弃执行,允许另一个作业在下一个时间片里执行。通过;这种方法可 以快速地在各个作业之间进行切换,形成了若干个作业同时执行的假象。依据所执行的作业的 类型,早期的分时系统能够同时为多达30个用户提供可接受的实时服务。今天,多道程序设计 既可用于单用户系统,也可以用于多用户系统,前者通常称为 多任务 ( multitasking )。 也就是说, 分时指的是多个用户共享对同一计算机的访问,而多任务指的是一个用户同时执行多个任务。 随着多用户的发展,分时操作系统作为一种典型配置,被用在大型的中央计算机上,用来 连接大量的工作站。通过这些工作站,用户能够从机房外面直接与计算机进行通信,而不用把 请求递交给计算机操作员。通常把常用的程序存储在计算机的海量存储设备上,然后通过操作 系统来响应工作站的请求并执行这些程序。这样,作为计算机与用户中间媒介的计算机操作员 作用就不那么明显了。 到今天,特别是在个人计算机领域,计算机用户已经能够承担计算机操作的所有职责。因此, 计算机操作员在事实上已经不存在了,即使大型计算机系统,其运行也基本上无须人工参与。 事实上,传统的计算机操作员已经让位于系统管理员,系统管理员管理计算机系统,获得和监 控计算机新设备和软件的安装,实施一些本地的规则,例如建立新的账号、为不同的用户划分一 定的存储容量、协调用户一起解决系统中出现的问题,这样就比纯手工方式操作计算机要好得多。 总之,操作系统已经从简单的一次获取和执行一个程序发展为能够分时处理,能够管理计 算机海量存储设备上的程序和数据文件,并能直接响应计算机用户请求的复杂系统。 但是,计算机操作系统的发展仍在继续。多处理器机器的发展已经让操作系统能够进行分 时/多任务处理,操作系统把不同的任务分配给不同的处理器进行处理,以及采用分时机制共 享单个处理器。这些操作系统必须处理负 载平衡 (load balancing , 动态地把任务分配给各个处 理器, 使得所有处理器都得到有效的利用) 和均分 ( scaling , 把大的任务划分为若干个子任务, 并与可用的处理器数目相适应)问题。 . 此外,计算机网络的出现(相距很远的大量计算机连接在一起)使得有必要发展相应的软 件系统来规范网络的行为。计算机网络领域(我们将在第4章学习这部分内容)在许多方面拓展 了操作系统这个学科,其目标是跨多个用户和计算机(而非单一的、孤立的计算机)管理资源。 操作系统的另一个研究方向侧重专用于特定任务的设备,如医疗设备、车载电子设备、家 用电器、手机或其他手持电脑。这些设备中的计算机系统称为嵌入 式系统 (embedded system )。 嵌入式操作系统通常能够节省电池电量、满足严格的实时截止时间,或在只有很少或完全没有 人为监管的情况下连续工作。在这些努力中,有代表性的成功系 统有: VxWORKS , 它由 Wind River 系统公司开发,在称为“精神和机会”的火星探索旅程中也发挥了 作用; Windows CE (* 就是众所周知的 PocketPC ), 它由微软开发; PalmOS , 它由 PalmSource 公司开发,主要用在手 持设备上。 。二/夺秕的功愈赛来 ㈣ 雖知 V : _挣单_蹲#峰锋冬,钵能拔决本興.篆辱 见的智 肩賴」 (::辨^如 im /: ■砑用#麟武獨 . 棄成多;每私衿筒而 ; 言^ :鬼可蜂參务心此々:贅能等私需滅 麵:的 攝作: h 系疏,: :不权 廟叙爹 理奏 降知知魏七批福 (♦: 资滅 r 疼要式持选威增承_言廳等:舜土角赛件智 ;以:皋择.衿舞静等 娜鱗: 奔_ ; 赛巍嘮总秦赛赛:幸 - ' J ■*■ F ,H J - H Lj l ■,’ ' -F I : k II 1 , , 1 1 M 'L L L ' , 卜, L - I I , -...a , u,..i: : ; .u. d..r .-■;,-:.!.■.■'; ;■ :;::■ .:;.^u::-:).... :'- ; '^-;v : 部 :.:■::■:::;-!;:: i : :::: :!■;-:. .::::;:!:: ■;: 1 : ■-.::■; :■::: -:,,!■::■ ::■,:.:■:;: 卜 ■; ; ■, ; ; : ' : " : 0 ^ ; i -;; ■'. .: _. , i\. ": .•••: ' : . .v.:._ •--. ; ... : '; ; " ■ ,,, ■. ; : ■: - " 」 ..-•••',::,^,- V :>:'- '-•!" ''":''-.- o :: , s .'''':. : -: •;-; ', ;;, ..: r . .._•. ■'■■': " A ' :,;. i ;.!,,'::•.'■::: '':':•,!';• .; w '-,:-;;; r : i,;,r ,,;;;,; ;;;;;;.:,;,,;• i .::. L .::.:.".. y :'. : ''•::;• • : ..;" i ' " • :,; :, ,,; ,;; ;, f ;,; - . _ : i ':..•:•:„'• .:.'.々 : _:■卜 :; i ■: 0 . .;^ i ' i ,! >■.■ ;^^;; i '-\ ij !;-';^^: : i : : i ^::.;:|:-,::! i i ^! : -^!^ i: ' - :_ ! i ; i |: : i - :'..::' -: !:;: ; Tj : - ;|; :: 年史淨卜 n 」.: 卜: :0; !: ,: : :; ':;^:. !:! :: ; :; :. . - ::; ■: ||:;-};|; : :: v ,^: : - : i ,! i : : ^: m :^:: ;::'::: !:^, j : i: - :, . : : ^:;: ,::,: ^::^'; i '^ : :!.)..'^!^ : ;.;. 1 ^..;-费:」:..;.:.:3:: ; ::;:「:丨: : 。 .. A : ㈡ 卜 i : . ^ :: 辠:#:出來个的催子。:对手秦 一 #情况,'请播出隹何可龍破坏 HP 嗎构的倩况^ ' 下列毪务中娜:些霽 要用到寒时* 理我术? . I 、 . I ;… 二」, H >神輝袢歹赢::: 「 …,… ■ 」:::: . … ■ O ' i 运评筹机_璋' v .:' :': : 1 ,心拨省齡迻些教寮麗示 ® 智俞 g 举雖彝,赛 i 二 _ ^ ' ' i : ' …' 乂 , '表, ㊉ 广个衰蜋下 一 #释齋? ik ® 辨程)%:、 …?: : V :::::- - :在:_踩_ 緣音 。卜…卜,…: V :".. :'. 1 ,: … ^ 1 / 1 :: :: :、、:」,: ' 4. 分翻多住务处理的区薄 jlig 杯么? : :/ , : ' . : f H 3-2 操作系统的体系结构 为了能够理解一个典型操作系统的组成,这里我们首先考虑一个典型的计算机系统中有哪 些软件,软件是如何分类的,然后我们再回到操作系统上来。 3.2.1 软件概述 我们通过提出一个软件分类方案考察一个典型计算机系统中的软件。这种分类方案总是把 一些似的软件单元放在不同的类里,其方法如同时区的划分^ (时区的划分使得相邻时区的设 3.2 操作系统的体系结构 83 置相差一小时,即使其日出与日落的时间没有明显的差别。)其次,在软件分类的情况下,学科 的发展变化和某种权威的缺乏,导致了一些矛盾的分类方法。例如,微软公司 Windows 操作系 统的用户会发现,“附件”和“管理工具”程序组,既包括应用类软件又包括实用类软件。因此, 下面的分类方法应该被看做在广泛的、动态的学科里占有一席之地的工具,而不是对人们普遍 接受的事实的一种表述。 先把计算机软件分为两 大类: 应用软件 ( applicationsoftware ) 和系 统软件 (system software ) (参见图 3-3 应用软件是由一些完成计算机特定任务的程序组成的。一台用来维护某个制造公 司库存单的计算机所包含的应用软件与电气工程师用的计算机里的软件是不同的。应用软件的 例子有电子制表软件、数据库系统、桌面出版系统、记账系统、程序开发软件以及游戏等。 图 3-3 软件分类 相对于应用软件而言,系统软件完成一般的计算机系统都需要完成的任务。在某种意义上, 系统软件提供了应用软件所需要的基础架构,这和国家基础架构(政府、道路、公共设施、金 融机构等)提供公民维系各自生活方式的基础的方式大致相同。 系统软件又可分两类, 一 类是操作系统本身,另一类是统称为实 用软件 (utility software ) 的软件单元。安装的大多数实用软件包括这样一些程序,它们实现的活动仅仅是计算机的安装 的基础,而没有包含在操作系统中。从某种意义上说,实用软件是由一些能够扩充(或定制) 操作系统功能的软件单元组成的。举例来说,格式化磁盘或将文件从磁盘复制到光盘中去的能 力仅仅是借助于实用软件,而不是在操作系统内部实现的。其他的实用软件的例子包括数据压 缩与解压缩软件、多媒体播放软件和处理网络通信的软件。 把某些工作作为实用软件来实现,允许定制系统软件,这比把它们交给操作系统来执行要 更容易满足特定安装的需求。事实上,一些公司和个人对原先和计算机操作系统一起提供的实 用软件进行修改和扩充,已经是很普通的事情了。 遗憾的是,应用软件与实用软件之间的差别已经很模糊。从我们的观点来看,它们的差别 在于其是否是计算机软件架构的一部分。因此,当新的应用变成了一种基础的工具,这个应用 就很可能成为一种实用软件。当用于因特网的通信软件还在研究阶段时,它就被认为是一种应 用软件,而在今天,像这样的工具软件对大部分 PC 机应用而言非常基础,也就被定义为了实用 软件。 实用软件和操作系统的差别同样是模糊的。特别是,美国和欧洲的反垄断诉讼案争论的都 是这样一个 问题: 浏览器和媒体播放器这两个组件是微软公司操作系统的一部分,还是微软公 司用来压制竞争对手的实用软件。 穿 『_顆 輝賴 ,? ..玢 :乎 聲參蝻 _ 參喊戴::;如矣磕:缚 _ ■芋靶^了#二命 第: __/ 外 limx 。 蕞杂的 Linux 孤择系统是由 Linus 知 rvalds 在赫扣辛基大学赛习瑜_设升的。 Linux # 作系统 是一个非专利产品,我们可以南赛获得它的源代码(见第6華)自关文挡*因为可 以免费获得甚源代码所以谖系统在计筹机爱好者、学习操雄系统的程序员中非常 流行。而且, Limix 操舞系统被认为是当今甘 用的 较可靠的操作系统之一 因为 这个原因, :: if *? P . :: '; 萍 I 裔義選和忠::彳遽 ti 袭:1邊韶 hi 烧!彳_驾法找&訖彳皂漢 B.?..:. : :::.;. 6 :.;;.:把汔I:;;;1: : : : ::;::41': ■;!:■:'■ "Ur : ■:: 攀 _._ 樣____||||___ : ^^___|迦 更多有关 Linux 的知识。 3.2.2 操作系统组件 现在,我们把注意力集中在操作系统领域内的组件上。为了完成计算机用户请求的动作, 操作系统必须能够与这些用户进行通信。操作系统负责处理这种通信的部分通常称为用 户界面 (user interface )。 老式的用户界面称为 shell , 通过键盘和显示屏用文本信息与用户通信。更现代 化的系统利用 GUI (Graphical User Interface , 图形用户界面)实现与用户的通信,其中操作的 对象(如文件和程序)被表示为显示屏上的图标 ( icon )。 这些系统允许用户使用某种常用的输 入设备发出命令。例如,有一个或多个按钮的计算机鼠标可用来单击或拖曳屏幕上的图标。另 外,平面设计师或某些类型的手持设备常使用专用的指示设备或手写笔代替鼠标来操作图标。 最近,高密度触摸屏的进步使得用户可以直接用手指操作图标。当今的 GUI 使用二维图像投影 系统,三维立体界面允许用户通过 3 D 投影系统、知觉设备和环绕音频再生系统与计算机进行通 信,它是当前研究的课题。 虽然操作系统的用户界面在实现计算机的功能上扮演了重要的角色,但它仅仅是用户与操 作系统内核之间的一个接口而已(见图3-4)。用户界面与操作系统内部之间的区别的呈现是因 为这样一个事实,即一些操作系统允许特定用户从各种界面中选择最合适的界面为自己服务。 例如, UNIX 操作系统的用户就可以选择不同的 shell , 包括 Bourne shell、C shell 和 Kom shell , 以 及称为 Xll 的 GUTL 最早的 Microsoft Windows 是一个 GUI 应用程序,可以通过 MS - DOS 操作系统 的 shell 命令加载。在最新版 Windows 中,人们仍可看见作为实用程序存在的 DOS shell cmd . exe , 但非专业用户几乎完全不需要使用这一界面。类似地,苹果公司的 OS X 保留了一个 Terminal 实 用软件 (utility shell ), 它承袭了系统的 UNIX shell 。 用户 夺 t 基 令 图 3-4 作为用户和操作系统内核之间中介的用户界面 3.2 操作系统的体系结构 85 今天的 GUI shell 中的重要组件是窗口管理程序 (window manager ), 该程序在屏幕上分配若 干块(称为窗口的区域),并且跟踪与每个窗口相联系的应用程序。当一个应用程序想在屏幕上 显示图像时,它就会通知窗口管理程序,窗口管理程序就会把图像放在分配给该应用程序的窗 口里。然后,当单击鼠标时,窗口管理程序计算鼠标在屏幕上的位置,并把这个鼠标动作通知 给相应的应用程序。窗口管理程序负责生成 GUI 样式,且大多数管理程序会提供一系列配置选项。 Lhmx 用户甚至可以选择窗口管理程序,常用的选项包括 KDE 和 Gnome 。 与操作系统的用户界面相对,我们把操作系统内部的部分称为内核 ( kernel )。 操作系统的 内核包含一些完成计算机安装所要求的极基本功能的软件组件。其中一个组件是文件管理程序 (file manager), 它的工作是协调计算机海量存储器设施的使用。更准确地说,文件管理程序维 护着存储在海量存储器上的所有文件的记录,包括每个文件的位置、哪些用户有权访问各种文 件以及海量存储器里的哪部分可以用来建立新文件或扩充现有文件。这些记录被存放在单独的 包含相关文件的存储介质中,这样,每次存储介质启动时,文件管理程序就能够检索相关的文 件,进而就能知道特定的存储介质中存放的是什么。 为了方便计算机用户,大多数文件管理程序都允许把若干个文件组织在一起,放在目录 ( directory ) 或文件夹 ( folder ) 里。这种方法允许用户将自己的文件依据用途划分,把相关的文 件放在同一个目录里。 一 个目录可以包含称为子目录的其他目录,这样就可以构建层次化的目 录结构。例如,用户可以创建一个名为 MyRecords 的目录,它又包含了名为 FinancialRecords 、 MedicalRecords 和 HouseHoldRecords 的3个子目录。每个子目录中都会有属于该范畴的文件。 ( Windows 操作系统的用户能通过执行 Windows 资源管理器程序,让文件管理程序显示当前所有 的目录结构。) ’ 一条由目录内的目录所组成的链称为目录路径 (directory path )。 路径通常是这样表 示的: 列出沿该路径的目录,然后用斜杠分隔它们。例如,路径 animals / prehistoric/dinosaurs 表示 的是: 该路径是从目录名为 animals 的目录开始的,经过名为 prehistoric 的子目录,终止于名为 dinosaurs 的子目录。(对于 Windows 用户而言,目录路径是用反斜杠表示的, Manimals \ prehistoric \ dinosaurs 。) 其他软件实体对文件的任何访问都是由文件管理程序来实现的。该访问过程是这样开始的, 先通过一个称为打开文件的过程来请求文件管理程序授权访问该文件,如果文件管理程序批准 了该访问请求,那么它就会提供查找和操控该文件所需的信息。 内核的另外一个组件是一组设备驱动程序 (device driver ). 它们是负责与控制器(有时直 接与外围设备)通信,以操作连接到计算机的外围设备的软件组件。每个设备驱动程序都是专 门为特定类型的设备 C 如打印机、磁盘驱动器和显示器等)设计的,它把一般的请求翻译为这 种设备(分配给这个驱动程序的设备)所需要的更富技术性的步骤。例如,打印机的设备驱动 程序包含的软件能够读取和解码特定打印机的状态字,而且还能够处理其他一些信息交换的细 节。这样,其他软件组件就没有必要为了打印一个文件处理这些技术细节,而只需要运用设备 驱动程序软件完成打印文件的任务,技术细节交由设备驱动程序处理。按照这种方式,其他软 件组件的设计可以独立于具体设备特有的特征。这样做的结果是,一个普通的操作系统能够使 用一些特殊外围设备,我们只要安装合适的设备驱动程序即可。 在操作系统的内核中,还有一个组件就是内存管理程序 ( memorymanager ), 它担负着协调 和管理计算机使用主存储器的任务 a 在计算机一次仅执行一个任务的环境中,这些工作就比较 简单了。这些情况下,执行当前任务的程序放在主存储器中已经定义好的位置上执行,然后被 执行下一个任务的程序替换。然而,在多用户和多任务的环境下,要求计算机在同一时刻能够 处理多个需求,这时内存管理程序的职责就扩展了。在这些情况下,许多程序和数据块必须同 时驻留在内存里。因此,内存管理程序必须找到并给这些需求分配内存空间,并且要保证每个 86 第 3 章操作系统 _ 程序只能限制在程序所分配的内存空间内运行。此外,随着不同活动的需求进出内存,内存管 理程序必须能跟踪那些不再被占用的内存区域。 当所需的总内存空间超过该计算机实际所能提供的可用内存空间时,内存管理程序的任务 会更复杂。在这种情况下,内存管理程序在内存与海量存储器之间来回切换程序和数据[称为 页面调度 ( paging )], 这样就造成了有额外内存空间的假象。例如,假设需要一块8 GB 的内存空 间, 但是计算机所能提供的只有 4 GB 。 为了 造成具有更大内存的假象,内存管理程序在磁盘上 预留了4 GB 的存储空间。在这块存储区域里,将记录内存实际容量有8 GB 时本应存储在内存中 的位模式。这块数据区被分成大小一致的存储单元,该存储单元称 为页面 ( page ), 典型的页面 大小只有几千字节。于是,内存管理程序就在主存和海量存储器之间来回切换这些 页面。 这样, 在任何给定的时间内,我们所需的页面都会出现在4 GB 的内存之中,最后的结果是计算机能够 像确实拥有8 GB 内存一样工作。这块由分页技术所产生的大的“虚构的”内存空间称作 虚拟内 存 (virtual memory ) 0 另夕 h 在操作系统内核中 还有调度程序 ( scheduler ) 和分派程序 ( dispatcher ) 这两个组件, 我们将在下一节介绍。在此,我们只需注意,在多道程序设计系统中调度程序决定哪些活动是 可以执行的,而分派程序控制给这些活动的时间分配。 3.2.3 系统启动 我们已经可以看出,操作系统提供了其他软件组件所需的软件基础设施,但是我们还没有 细想操作系统本身是如何启动的。这是通过一个称为引导 (boot strapping , 简称为 booting ) 的 过程实现的,这个过程是由计算机在每次启动的时候完成的。正是这个过程把操作系统从海量 存储器(它永久存放的地方)传送到主存储器(在开机时,内存实际上是空的)中。为了理解 启动过程和必须有启动过程的原因,我们先来考察计算机的 CPU 。 CPU 的设计使得每次启动时,程序计数器都从事先确定的特定地址开始。 CPU 就在这个地 址上期望能找到程序要执行的第一条指令。从概念上讲,只需在这个地址上存储操作系统。然 而,从技术上讲,计算机的主存通常是采用易失性技术制造的,当计算机关闭时,也就意味着 存储在内存上的数据会丢失。因此,在每次重启计算机的时候,我们就需要一种重新填充主存 的方法。 简言之,当计算机首次打开时,我们需要在主存储器中有一个程序(最好是操作系统),但 是每次关机后,计算机的易失性存储器都会被檫除。为了解决这个两难问题,计算机的一小部 分主存储器就用非易失性存储单元建造,而这地方正是 CPU 期望找到初始程序的地方。由于这 种存储器的内容可以读取,但不可以改变,因而被称为只读 存储器 ( ROM )。 打个比方,虽然 所使用的技术是更先进的,但我们可以把在 ROM 中存储位模式想象成熔断微小的保险丝(熔断 的表示1,未熔断的表示0)。更确切地说,如今个人电脑中大多数的 ROM 是用闪存技术构建的 (即不是严格意义上的 ROM , 因为它可以在特定情况下被改变)。 在一般的电脑中, 引导程序 (boot loader ) 被永久存储在机器的 ROM 中。这样,在计算机 开机的时候将最先执行这个程序。引导程序的任务是引导 CPU 把操作系统从海量存储器中预先 定义的位置调入主存的易失性存储区(如图3-5)。现代的引导装载程序可以复制各种位置的操 作系统到主存储器。例如,在嵌入式系统如智能手机中,操作系统是从闪存(非易失性存储 器)复 制的; 在大型公司或大学的小型工作站上,可能要通过网络从远程机器上复制操作系 统。 一旦操作系统被放入主存,引导程序就引导 CPU 执行跳转指令,转到这个存储区。这时, 操作系统接管并开始控制计算机的活动。执行引导程序和启动操作系统的整个过程称作启动 ( booting ) 计算机 Q 3.2 操作系统的体系结构 87 主存储器 引导程序 1 易失存一 储 g 磁盘存储器 操作系统 步骤1:机器由执行已在存储器中的引导程序开 始启动。操作系统存放在海量存储器中 图 3-5 ROM 主存储器 操作系统 易失存储区一 磁盘存储器 _ 操作系统 步骤2:引导程序把操作系统传送到主存储器中, 并把控制权交给它 引导过程 除了引导程序外, PC 机的只读存储器还包括了一组例行程 序,, 用-于实现恭本的输入/输 出濟嘩,命从键盘上接峰信惠、把偉息,暮示在计算机的畢幕 J;, 以4从—囊華雜葬^读輿据 等。因为存放在#易失性存4器 ( 如 FlasURdM) 中,所以逵些例行輕屬并不 i 本变地 In 化到 机器的座片(硬件)中,也不像海量存嫜中的其他程序(软件)那样随时可被更政。人们创 造了固件 (firmwate) 这个词来描述这一 “中间_带' 固件例行程序可以被引等程序使用, 以便在揲作系统开始工作前完成 I / O 活动。例如,噹们会在引导过程真正开始前,用于 与计算 机用户通信,并在引导期间提交错误掖告。得到广泛使用的固件 系统包 括 PC 中一直 使用的 綱麵; _ 義麵 • 囊鬚: 许多嵌入式设备的 CFE ( Common Firmware Environment , 通用固件环境)。 你也许在想,为什么台式计算机不提供足够的 ROM 来装载整个操作系统呢,这样从海量存 储器来引导启动就没有必要了。虽然对于使用小型操作系统的嵌入式系统而言这是可行的,但 就当今的技术而言,把通用计算机的大块主存专用于非易失性的存储,效率就不高了。另一方 面,计算机操作系统要频繁地进行更新,以确保安全并与最新硬件改良了的新设备驱动程序同 步。虽然也有可能去更新存储在 ROM 中的操作系统和引导装载程序^■通常称为固 件更新 ( firmwareupdate ), 但技术上的限制使得海量存储器成为了较传统的计算机系统的最普遍选择。 最后,我们要指出,理解引导过程以及操作系统、实用软件和应用软件之间的区别,能帮 助我们更好地领会大多数通用计算机系统操作的运行方法。当这样的计算机第一次开机时,引 导过程装入并激活操作系统,然后用户向操作系统提出请求,执行实用软件或应用程序。当实 用软件或应用程序终止时,用户再次获得与操作系统的联系,这时用户能提出另一次请求。因 此,学习使用这样的系统是一个双层过程,除了学习指定实用软件或期望的应用程序的细节之 外,还必须学习足够多的关于计算机操作系统的知识,以便能游刃有余地切换应用程序。 问题与_习 1. 列举與型操作系统的组件,并用一句话概括每个组件的作用。 :2.. 应用_与实用软件之间的区别是什么? . , 3,. 什么 是虚拟 存储器 ' ■ •: ! ' •:":' ^ ';:::::: : .• :;••':. :.: - ' '; : i ! : , i : ' .:■ W . ;; L ;. :4.槭速.議^程 6 1 88 第 3 章操作系统 岁谷丁::名茂纪啦;;饮印财: i 扣嫩如 3.3 协调机器的活动 U 扪; 卜.:11功(.设::_料:1故扣:娜功 ty :!^ 肿:蛛;似 ii 乃娜!^:出郎然5^&料挪:£^令:: 二 4!: 們祇叫 Whr i ^^ r : 托 本节我们讨论操作系统如何协调应用软件、实用软件以及操作系统自身内部单元的执行。 首先,从进程的概念开始。 3.3.1 进程的概念 现代操作系统的一个最基本概念就是程序与执行程序的活动之间的区别。前者是一组静态 的指令,而后者是一个动态的活动,其属性会随着时间的推进而改变。(我们可以把程序想象成 静静夹在架子上的一页乐谱,而把活动想象成演奏这段音乐的音乐家。)在操作系统的控制下执 行某个程序的活动称为进程 ( process )。 与进程联系在一起的活动的当前状态称为进程状态 (process state ) 0 这个状态包含正在执行的程序的当前位置(程序计数器的值)、 CPU 中其他寄存 器的值以及相关的存储单元。大约说来,进程状态就是机器在特定时刻的 快照。 在程序执行期 间的不同时刻(一个进程中的不同时刻),将观察到不同的快照(不同的进程 状态乂 与音乐家一次仅尝试演奏一份音乐作品不同,在典型的分时/多任务计算机中通常会有许多 进程同时在执行并竞争计算机资源。操作系统的任务就是管理这些进程,使每个进程都能获得 其需要的计算机资源(外围设备、主存空间、访问文件以及访问 CPU ), 确保独立进程不会相互 干扰,确保需要交换信息的进程能够进行信息交换。 3.3.2 进程管理 与协调进程的执行有关的任务是由操作系统内核中的调度程序和分派程序处理的。调度程 序维护一个有关计算机系统中现存进程的记录(也就是进程池),将新的进程加入到该进程池中, 并把已经完成的进程移出进程池。这样,当用户请求执行一个应用时,调度程序就把这个应用 加到当前进程池加以执行。 为了跟踪所有的进程,调度程序在主存中维护着一个信息块,称为进程表 (process table )。 每当要请求程序执行时,调度程序都在进程表中为该程序创建一个新的表项。这个表项包含有 如分配给该进程的存储区(从内存管理程序得到)、进程的优先级以及该进程是处于就绪状态还 是等待状态这样的信息。如果进程能够继续执行,那么该进程就处于就绪 ( ready ) 状态; 如果 进程因为要等待某个外部事件的发生而中断,例如海量存储操作的完成、等待键盘的按键以及 等待其他进程传来的消息等,那么该进程就处于等待 ( waiting ) 状态。 分派程序是内核的一个组件,它确保被调度的进程能实际执行。在分时/多任务系统中,这 个任务是依靠多道程序设计 ( multiprogramming ) 来完成的,也就是说,先将时间划分为小的时 间段,每段称为一个时间片 (time slice ), 通常毫秒或微秒计量,然后把 CPU 的注意力放在就绪 进程上,允许每个进程一次执行一个时间片(参见图3-6)。这种从一个进程到另一个进程的改 变过程称为进程切换 (process switch ) 或进程上下文切换 (context switch ) 。 每次分派程序给进程分配一个时间片,它都会初始化一个计时器电路,通过产生一个中断 ( interrupt ) 信号来指示时间片的结束。 CPU 对中断信号的响应方法就如同你被一个任务打断时 的应对方法,你停止当时正在做的工作,记录当时任务进展的位置(这样就能在以后返回到被 中断工作的接续位置),然后处理中断事件。当 CPU 收到一个中断信号时,它会完成当前的机器 周期,保存它在当前进程中的位置,然后就幵始执行称为中断处理程序 (interrupt handler ) 的 程序,该程序存放在主存中的预先定义的位置上。中断处理程序是分派程序的一部分,它用来 描述分派程序如何响应中断信号。 3.3 协调机器的活动 89 时间轴4 时间片 时间片 时间片 时间片 图 3-6 进程 A 与进程 B 之间的多道程序设计 ' 4 .■ X: 1 ? ^ / : 1.:, -V . -J : - J-, W . 1 乂 史: -- f , -V:__ •, u 八 觀讎:.聽號雜 ㉗ P 啡喊翁_ 攀,;銜, _ "■ ~^- _ T * JV , ;.: ,十 a , 敏 •■. :-十;久 7.’ 1 ’ 中断是用来终止时间片的,正如本文中所描述的一样,这只是计算机中断系统中众多应 用中的一个。有许多可以 产生中 断信号的环境,每个都有自己的中断刼程 5 事卖上,中断为 协调计算机的活动与相关环境提供了一个重要的工具。例如,单击鼠标和按下键盘中的一个 按键都能产生一个中断信号,这样就能使 CPU 放下正在处理的工作,转而去解决中断。 为了管理识别和响应中断的任务,不同的中斯信号被赋予了不同的优先级,这样一来, 最重要的任务最袭餐到关洚。最高级别的中新通常与电源故障有关,義计算机电源意外中新 而产生的中断信号等。然后,::在几毫秒时间 内:, 与之相关的:中斯衅理■癌赶在电具_;不儀:; ,::”邋 雜截献: 家奢溱 S :::的繁杂— . J ::: :,::嘯 I :::: . :^; vi ! " 于是,中断信号的作用就是取代当前进程,将控制权传回分派程序。此时,分派程序从进 程表的就绪队列(由调度程序决定)中选择优先级最高的进程,重启计时器电路,使被选择的 进程开始它的时间片加以执行。 多道程序设计系统能够成功的最大关键是能够停止进程,并且稍后能重启进程。如果你在 读一本书的时候被打断了,那么你能否在稍后继续阅读就依赖于你是否记得中断时读到的位置, 以及你是否记得那个位置之前的内容。简而言之,你必须能够重新建立起中断前所在的那个 环境。 在一个进程的情况下,必须重新建立的环境就是进程的状态。回想一下,这个状态包括程 序计数器的值以及寄存器和相关存储单元的值。在为多道程序设计系统开发的 CPU 中,保存这 种信息的任务是 CPU 应对中断信号的工作的一部分。这类 CPU 还提供机器语言指令,以重新装 入先前保存的状态。这种特性简化了分派程序完成进程切换的任务,它也例证了现代的 CPU 设 计是如何受当今操作系统的需求影响的。 最后,我们应当注意到,多道程序设计是为了提高计算机总体效率的。这有点违反常理, 因为多道程序设计要对进程来回切换,因而会产生一定的开销。但是,如果没有多道程序设计 处理技术,那么每个进程在下一个进程开始之前完成执行,这也就意味着进程等待外围设备来 完成任务,或者用户发出下一个请求的时间被浪费了。多道程序设计技术可以把这些丢失的时 间给其他进程。例如,如果一个进程执行 I / O 请求,如向磁盘提出读 数据请 求,那么调度程序就 会更新进程表来反映出这个进程正在等待外部事件。结果是,分派程序将不再给该进程分配肘 间片。之后(也许是几百毫秒),当 I / O 请求完成时,调度程序将会更新进程表来显示该进程处 于就绪状态,这样这个进程就可以重新竞争时间片了。简而言之,当正在执行 I / O 请求时,程序 90 第 3 章操作系统 可以执行其他的任务,因此一组任务的完成时间要比按照顺序方式执行所花的时间少。 1 _ 概述程序和进程的差别。 # 顧雄在中職出■时 r 寒壳成 哪些捩 零 , m 3. 在多遒程序设计^中,如何能_优先级的进程运行得比其他进程杂? 4. 在一个多道程序设计系统里,如果每个时间片是 SO ms , 每次上下文切換所花费的时间最多是1妒,那 么计算机在1 s 内爾_斟务學多少剩: V 二 5. 在练习4中,如果每个擊程都完全使用了它的时间片,那么实际花费在进程执行上的时间占整个机器 :时间撖比侧是多少?如果每个迸程在它的时间片后的$邮执行 I / O 请求,,那么这•比倒又是多少? *3.4 处理进程间的竞争 操作系统的一个重要任务就是将机器的各种资源分配给系统中的各个进程 。 从广义.匕讲, 我们所用的资源 ( resource ) 这个术语,不仅包括机器的外围设备,还包括机器本身的特性。文 件管理程序分配对文件的访问并为新建立的文件分配磁盘空间,内存管理程序分配内存空间, 调度程序分配进程表的空间,分派程序分配时间片。正如计算机系统里的许多问题一样,这种 分配任务表面上看起来很简单,但实际上,对于没有设计好的操作系统,几个微小的错误将导 致系统的故障。要记住,计算机不会自己思考,它仅仅是遵照指令办事。因此,为了构建一个可 靠的操作系统,我们必须设计算法以克服各种可能出现的意外情况,不管它出现的概率有多小。 3.4.1 信号量 考虑一个分时/多任务操作系统,它控制只有一台打印机的计算机的活动。如果一个进程要 求打印结果,那么它必须向操作系统提出请求,要求访问打印机设备的驱动程序。这个时候, 操作系统必须根据该打印机是否被其他的进程占用来决定是否批准这个请求。如果没有被占用, 那么操作系统应该批准这个请求,并允许该进程继续 执行; 否则,操作系统应当拒绝这个请求, 也许把这个进程归类为等待进程,直到打印机可用为止。如果有两个进程同时获得对打印机的 访问权,那么结果对两者都是不可取的。 雜 通过执行任务管理器藏个应用程释可以对微软 Wi 3 jdQ \^ s 無作系统的内部活动获得深 刻刼 f 解。(同时按下 Ctrl 、 Alt ^ Delete^c ) 特另11地,遒过选#任务誊瑄器窗口的进程标签, 你可以看到进程表。在此,:你可以体验 一下: 在激活任何症用程序之前,,看一下进程表^ (你 也坪会惊讶地发现表中已鉍#如此多的进释,它们 对于糸 統的基本应用都是必不可少 的二) - 现在激 活一个应用,并且确认一个新进 程色经 进入到表中。你将还能够着到分配给进程的 存储冢.間:量為 . 攀'..泰 I •!::;•• ii:.::..-:.. - ' ••:••: : .:•:::•• :•::: i 1 •:• : •: ... .. .... ..i •- .. 为了控制对打印机的访问,操作系统必须跟踪打印机是否已经被分配。解决这个任务的一 种方法是使用一个标志。在这里,它指存储器中的一个位,其状态通常是指置位 ( set ) 和清零 ( clear ), 而不是1和0。清零标志(值为 0) 表示打印机可用,置位标志(值为 1) 表示打印机当 前已经分配出去了。表面上看,这种方法似乎可行。每次访问打印机的一个请求到来时,操作 系统要做的工作仅仅是检查这个标志位。如果是清零标志位,那么操作系统就批准该请求,同 *3.4 处理进程间的竞争 91 时将标志位进行置位。如果标志位已经置位,操作系统就将提出请求的进程放入等待队列中。 每当一个进程完成了访问打印机的任务,操作系统要么将打印机分配给一个等待进程,要么在 没有等待进程时,将这个标志清零。 然而,这个简单的标志系统还是有个问题。测试和可能有的标志置位任务也许需要几条机 器指令。(从主存得到标志的值,在 CPU 中操控,最终写回主存。)因此,在检测到清零标志之 后和标志被置位之前,这个任务有可能被中断。具体而言,假设这个打印机当前是可用的,且 一个进程请求它的使用权,标志从主存中找到,而且发现它已清零,表示该打印机可用。但是, 在这个时候这个进程被中断了,另一个进程开始了它的时间片,它也请求打印机的使用权。于 是再一次从主存检索标志,仍发现它是清零的。因为前一个进程在操作系统要求从主存置位标 志之前被中断了。因此,操作系统允许第二个进程使用打印机。过后,第一个进程在它被中断 的地方恢复执行,那个地方正是操作系统发现标志是清零的地方。于是,操作系统继续对主存 中的标志置位并允许第一个进程访问打印机。现在,这两个进程在使用同一台打印机。 这个问题的解决办法就是,要坚持让测试和可能有的标志置位任务必须在没有中断的条件 下完成。一种方法是使用大多数机器语言都提供的中断屏蔽指令和中断允许指令。在执行时, 中断屏蔽指令使未来的中断被锁定,而中断允许指令则使 CPU 恢复对中断信号的响应。于是, 如果操作系统用中断屏蔽指令开始一个标志测试例程,并以中断允许指令结束,那么该例程一 旦开始就不会有其他活动中断它。 另一种方法是使用测试 并置位 ( test - and - set ) 指令,它在许多机器语言里可用。这条指令要 求 CPU 检索一个标志的值,记住它,然后置位该标志,所有工作都在一条机器指令内完成。它 的优点是,因为 CPU 在辨认一个中断之前必须完成当前的指令,所以测试任务和标志置位作为 一条指令实现时不可能被分割。 刚才描述的一个正确实现的标志称为 信号量 ( semaphore ), 它源自于控制轨道区段使用的 铁路信号灯。事实上,信号量在软件系统里的用法与信号灯在铁路系统里的用法是一样的。就 像一个轨道区间一次只能有一列列车, 一 段指令一次也只被一个进程执行。这样一段指令称为 临界区 (critical region )。 一个临界区一次只允许被一个进程执行,这个要求称 为互斥 (mutual exclusion ) o 概括地说,获得对一个临界区的互斥的常用办法是用一个信号量守护这个临界区。 一个进程要进这个临界区,必须确定这个信号量是清零的,并在进入临界区之前把它置位,然 后在出临界区时把这个信号量清零。如果发现这个信号量在置位状态,那么试图进入临界区的 进程必须等待,直到这个信号量被清零。 3.4.2 死锁 在资源分配中可能发生的另一个问题 是死锁 ( deadlock )。 在死锁状态下,两个或更多的进 程被阻塞,不能执行,因为它们中的每一个都在等待已分配给另一个的资源。例如,一个进程 可能已有对打印机的访问权,同时它还在等待访问计算机的 CD 播放器,而另一个进程有 CD 播 放器的访问权,却在等待访问打印机。另一个例子出现在允许进程创建新的进程[这种活动在 UNIX 术语中称 为创建子进程 ( forking )] 来完成子任务的系统里。如果调度程序因为进程表没 有空间而无法创建新的进程,同时系统里的每个进程又都必须创建额外的进程才能完成任务, 那么没有一个进程可以继续。这种条件下(见图 3-7) 严重降低了系统的性能. 死锁状态的分析已经揭示,只有以下3个条件全部满足它才会出现。 (1) 存在对不可共享资源的竞争。 (2) 这些资源是在不完整的基础上请求的。也就是说, 一 个进程接受了某些资源后,稍后 还将请求其他的资源。 92 第 3 章操作系统 (3) 一个资源一旦被分配出去,就不能以强制的办法再收回。 图 3-7 由于竞争不可共享的铁路区间造成的死锁 分离出这些条件的意义在于,只要努力抑制这三个条件当中的任何一个,就可以避免死锁 问题。一般认为,着力于抑制第三个条件的技术属于死锁检测和改正方案的范畴。在这些情况 下,死锁状态被认为不大容易出现,因而不必特别釆取办法避免死锁,而只是在死锁出现的时 候检测出它,然后通过强制性收回某些己经分配出去的资源来改正它。进程表已满就属于这种 情况。如果死锁是由于进程表满产生的,那么操作系统中的例程(或管理员利用其“超级用户” 的特权)可以移除[专业术语是清除 ( kill )] 某些进程,这将释放进程表的空间,打破死锁并 使得剩下的进程可以继续它们的工作。 着力于抑制前两个条件的技术,一般被称为死锁避免方案。例如,针对第二个条件的一个 方法是要求每个进程一次性请求它所需要的全部资源。另一个技术针对第一个条件,它不是直 接地消除竞争,而是把不可共享的资源转变为可共享的资源。例如,假定出问题的资源是打印 机,各种进程都请求使用它。每当一个进程请求打印机时,操作系统都会批准这个请求。但是, 操作系统不是把这个进程连接到打印机的设备驱动程序上,而是连接到一个“虚构”的设备驱 动程序上,该驱动程序把要打印的信息存放在海量存储器上,而不把它们发送到打印机上。于 是,每个进程都认为它访问了打印机,所以能正常工作。以后,当打印机可用时,操作系统可 以把数据从海量存储器传送到打印机。按照这个方法,操作系统通过建立多个虚构的打印机把 不可共享的资源变成了好像是可共享的。这种保存数据供以后在合适的时候输出的技术称为假 脱机 ( spooling )。 ■ "倖统 命时緣 锋萎系 娩通过 ㈣ 贼过人类 i 知的:速 度蛛速切痍耔 侖外辦请语如行 多个 进程的假象。现代系统继续通过这种方式实现多任务处理,但最新的7多核 CPU 确实能够同时 运行2个、4个或更多个进程。与一组协 W 工作的单核计_不同,一台多核机器包含多个独 立的处理# (称为核) ,七® 共享计算机的外围设备、内存等资源。对辛‘个多核 操作豕 统, 这意味着分旅程序和调度雍序必须考虑在每个核上应该执行哪些进程„ _不同的进程运行 于不同的核上,进程间的处理竞争变得更具有挑战性,因为每当一个进程需要进入临界区时, 所有核上都会禁用中断,但这种做法效率极低。构建能更好适用新的多核环境的操作系统机 制,是计算机科学中比较活跃的研究才向。 3.5 安全性 93 假脱机是一种允许多个进程访问一个公共资源的技术,它可以有许多变体。例如,文件管 理程序可以批准若干个进程访问同一个文件,前提是它们只是从该文件读取数据-如果多个 进程试图同时更改一个文件就会发生 冲突。 于是,文件管理程序可以根据进程的需要分配对文 件的访问权限,允许若干个进程有读访问权,但在任何给定时刻只有一个进程有写访问权。其 他的系统可能把这种文件分成区段,使得不同的进程可以并发地更改文件的不同部分。然而, 其中每一项技术要得到一个可靠的系统,都有一些枝节上的问题亟待解决。例如,当有写访问 权的进程更改了这个文件,如何通知那些只有读访问权的进程呢? 纖 麵圓議| 讎 L 假荦迸.程 A 和 B 共書同一台机器的时间,并且每个进程都龠要短紂间禮甩同一个不 可共事 的餐源 9 ^彳例: 郊,每个进程可能都打印一系列独立前攀拇吿。 .) 每个 进程輪 重复地無得迳个资泰释碑它, 稍熵又 ' 苒次请求它 e 按賦 TS 讀的方法控制对该资源的访 N 存在什么 缽点? ' 丼始时,:給一 个标為 鱗予值如果_輕>请求这个资源并直该标:志为4那么就批准蜂个请求,苦则 , 1 使进绛予#待我态 p 如果进程 B 请求这个资蟬并标志为1,,郑么就批准逸个请秦 否谢 使进程 吵于祕状态, • 畢当进轾 A ■完成对这个资源的访问,就把标志变为 U 每会 难稽与完成对逵个赉淼的 2,假定二条双歩邊的擔路在过隧瑋时奋并为一个举 道沩: 了协璃逵#_逭的裤甩,安装:: T 节逨偉号湞统 v 一辆汽车龙论从哪个八口进入隧遗,都舍使釀道入口处上者的—*:森奪 y 舞為_戒牟#斗舞道 ,时 ,,红 . 知舍灭 v 如果一辆到达的汽车发现红灯亮,那么它就要 等待、 直到灭时去年进人隧邊。、 1 这个系统存在什冬问题? 1 \ 1 ^ : 为 十解决 筚疗桥 本两铜 车才目遇的死鳞问题,假设已提出下爾几个解铐穷案。:说+獬决商秦考 4 除 lliilMliilliSillSlliiK:: :||_||___|_________1:議_^ S 國!麗圓着_圍麵議画::3圍[圓戀 Si 4. 假定我们用圓4表示多道_#设计系雛中的攀丁个进私 A 第_个圆荔到第;I个圓点故窬头泰未第一 个(圆点所 表示的 > 迸-^持(第二个圆点所表示 y 进程16在使角也 乂本無 数学 家把# 到 的这种图称为有向图 (directed grapk) ,有向 ,圈的 什么性彌_掛于操作系璋的知锁间题? 1 rwfi | ijC;'S ! S!iS ! 6li | :5- i &'« - 3.5 安全性 由于操作系统管理着计算机的活动,很自然,它也在维护安全性方面起了重要的作用。从 完整意义上说,安全性自身也有多种表现形式,可靠性就是其中一种。如果文件管理程序的缺 陷使得一个文件的一部分丢失了,那么这个文件就是不安全的。如果一个分派程序里的缺陷导 致系统故障(通常称为系统崩溃),使得一小时的打字工作白费了,那么我们会说,产品是不安 全的。因此,计算机系统的安全性需要一个设计完美的可信赖的操作系统。 可靠软件的研发不再受制于操作系统,它贯穿整个软件开发过程。在计算机科学里称为软件 工程,我们将在第7章讨论这个论题。在本节,我们集中讨论与操作系统息息相关的安全性问题。 3.5.1 来自机器外部的攻击 操作系统的一个重要任务就是,保护计算机的资源,防止受到非授权用户的访问。在不同 的人使用计算机的时候,操作系统一般通过为不同的授权用户建立“账户”的方法来标记不同 94 第 3 章操作系统 __ 权限的用户。账户实际上是包含了诸如用户的姓名、登录密码和用户的权限等条目的记录。操 作系统在每个登录 ( login ) 过程中使用这些信息控制他们对系统的访问权限。(登录过程是一系 列事务活动,在这个过程中,用户建立与计算机操作系统的初步联系。) 账 户由超级用户 ( superuser ) 或 管理员 ( administrator ) 创建。在登录过程中通过了操作系 统的管理员身份验证(通常是通过用户名和密码)的用户将享有很高的访问权限。这种联系一 旦建立,管理员就可以更改操作系统的内部设置、修改关键的软件包、调整其他用户访问系统 的权限,进行各种各样一般用户不能进行的活动。 通过这种“高级地位”,管理员能够监视计算机系统中的活动,检测到不管是恶意还是偶然 的破坏行为。为了巩固这种关系,人们开发了大量 称为审计软件 (auditing software ) 的软件实 用程序,来记录和分析发生在计算机系统中的活动。特别地,审计软件可以确定许多试图用错 误的密码登录系统的活动,指示出非授权用户试图获得计算机访问权的行为。审计软件还可以 识别用户账户中与该用户以往行为不一致的活动,这可能表明一个非授权用户访问了这个账户。 (以下这样的事情不太可能 发生: 一个用户,以前仅仅使用文字处理和电子制表软件,然后突然 幵始访问系统的很专业的应用软件,或者试图执行超过其权限范围的实用软件包。) 设计审计软件的另外一个目的是检测嗅 探软件 (sniffing software ) ,这种软件在计算机中运 行时能够记录活动并稍后将之报告给潜在的入侵者。举一个老的但是很有名的例子,如果一个 程序能够模拟操作系统的登录过程,那么这个程序就能被用来欺骗操作系统的授权用户,使他 们认为自己是在和操作系统通信。然而,实际上他们是在和一个冒名顶替者通信,并在将自己 的用户名和密码提供给冒名顶替者。 在所有与计算机安全相关的复杂技术问题上,让很多人感到吃惊的是,计算机系统安全领 域中的主要难题之一就是用户自己的不小心。例如,用户选择的密码相对比较容易猜(如名字 和生日 等); 告诉朋友自己的 密码; 没有定时更换自己的 密码; 将自己的离线海量存储设备在机 器间来回地转移,这样就潜在地降低了其安 全性; 在计算机系统中安装未经许可的软件,从而 有可能损坏系统的安全性。对于上述问题,大多数使用大量计算机的机构都采用强制的策略, 明文规定用户的需求和职责。 3.5.2 来自机器内部的攻击 一旦入侵者(也可能是怀有恶意的授权用户)获得了系统的访问权限,那么他们下一步的 工作通常是浏览机器,寻找其感兴趣的信息或者是找地方插入破坏性软件。如果一个入侵者获 取了系统的管理员账号,那么上述过程的发生就很自然了,这也是我们为什么要严格保护好管 理员密码的原因。然而,如果是通过普通账号进行访问,那么入侵者必然会欺骗操作系统,以 获得未授予该用户的权限。例如,入侵者会尝试着欺骗内存管理程序,让一个进程访问其被分 配的存储区以外的内存 区域; 或者欺骗文件管理程序,访问本应该无权访问的文件。 今天, CPU 在设计时已经加强了一些功能,能够阻止上面谈到的攻击尝试。举一个例子来 说,我们可以考虑这样一个 需求: 通过内存管理程序,将进程限制在给它分配的内存区域内。 如果没有这样的限制, 一 个进程就能够从内存中覆盖掉操作系统,从而接管对计算机的控制。 考虑这样的一种威胁,为多道程序设计系统设计的 CPUil 常包括若干个专用寄存器,操作系统 可以在这些寄存器中保存分配给一个进程的存储区域的上下界。于是,当执行该进程时 , CPU 把每个存储器引用与这些寄存器中的值进行比较,以保证该引用在指定的界限之内。如果发现 这个引用在为该进程指定的区域之外, CPU 将自动把控制权交还给操作系统(借助于中断处理), 这样操作系统可以作出合理的处理。 复习题 95 这个方案中还存在一个小的但很重要的问题。如果没有进一步的安全措施,一个进程还是 能够访问指定区域以外的内存单元,只要改变含有存储区界限的专用寄存器的值即可。也就是 说, 一 个进程想要访问更多的内存区域,它只需要增加存放上界的寄存器的值,然后不需要得 到操作系统的批准,就可以使用这些额外的内存区域。 为了防止这种恶意的活动,将多道程序设计系统的 CPU 设计为工作在两种 特权级 (privilege level ) 之一的模式下。我们将其中之一称为“有特权模式”,而另外一个称为“无特 权模式”。当处在有特权模式下时, CPU 能够用自己的机器语言处理所有的指令,但处在无特 权模式下时,能够接受的指令就是有限的。这种仅在有特权模式下可用的指令,我们 称为特 权指令 (privileged instruction ^ (典型的有特权指 令有: 改变内存界限寄存器的内容的指令和 改变 CPU 当前的特权模式的指令。)当 CPU 处于无特权模式时,任何执行特权指令的企图都 将引起中断。这个中断将 CPU 转变为有特权模式,并将控制权交给操作系统内部的中断处理 程序。 当开机时, CPU 处于有特权模式,因此操作系统在引导过程后开始启动时,所有的指令都 可以执行。然而,每当操作系统允许一个进程开始执行它的时间片时,就通过执行“改变特权 模式”的指令,将 CPU 切换到无特权模式。于是,如果一个进程试图执行有特权指令,操作系 统就会得到通知,这样操作系统就充当了维护计算机系统完整性的角色。 有特权指令和控制特权级别是操作系统维护安全性可用的一个主要工具。然而,使用这些 工具对操作系统设计而言是一项复杂的任务,且在当前的操作系统中,错误还在不断出现。因 此,在特权级别控制中,任何一点疏忽都可能给灾难打开大门,不论是恶意程序引起的,还是 无意中的程序设计错误造成的。如果允许一个进程更改控制多道程序设计系统的计时器,那么 这个进程就能够延长它自己的时间片,甚至控制整个机器。如果允许一个进程直接访问外围设 备,那么它就能不受系统文件管理程序的监管而读取文件。如果允许一个进程访问分配给它的 区域之外的内存单元,那么它就能访问甚至更改由其他进程正在使用的数据。因此,维护计算 机的安全性,既是管理员的一个重要任务,也是操作系统设计的一个目标。 问题与练习 : •:•: ""•:':•'• : r 1 : rr..:... : : .:• •,'•.••••: ...... . r : V ' - - ' :; .':•;. ' : - :'•:••:: , ,•:;••;:•,' , " •' -• .••'••••' '• : ... •' '• : J : •' :•: :- •: .V:"::,.:- .• •- : :. f -:.r '.:••• : ,.;• ,,; ... 1 . 列举几个密码逸取不好的例子,并说明为计么不好6 ; :: :: 2. 秀特尔奔-系列处逼捧 挺供 4个特权级别,为什么 CPU 的设计 人韻选择捤供4个 特校级 释,而不是3个 ||_顚_議麵 3. 如果多道程序设计系统里的一个进程可以访问分配给它的区域之外的存储#充,3| : 么它 M 样获得该机 . 器的控制权? - , - “ y . jl ^ 'VA R 白 :- y . f *',': 复习题 (带*的题目涉及选读小节的内容。) 1. 列出一个典型操作系统的4个活动。 2. 概述批处理和交互式处理的区别。 3. 假设有3个作业 R 、 S 、 T , 按这个顺序排在一 个作业队 列里; 接着,在第4个作业 X 进入队 列之前,1个作业移出了队列,然后又有1个作 业移出了队列,作业 Y 和作业 Z 排进队列,最 后,按照一次一个作业地顺序移出,使队列变 空。请按移出的顺序列出所有的作业。 4. 嵌入式系统和 PC 的差别是什么? 5. 什么是多任务操作系统? 6. 如果你有一台 PC , 列举它的几个多任务功能 给你带来方便的情形。 96 第 3 章操作系统 7. 根据你所熟悉的计算机系统,列举两个应用软 件组件和两个实用软件组件,然后说明为什么 这样归类。 8. a . 操作系统的用户接口的作用是什么? b . 操作系统的内核的作用是什么? 9. 路径 X / Y / Z 描述的是什么目录结构? 10. 定义橾作系统环境下使用的术语“进程”。 11. 操作系统的进程表里包含什么信息? 12. 就绪进程和等待进程的差别是什么? 13. 虚拟存储器和主存储器之间的差别是什么? 14. 假设某计算机有512 MB 的主存,操作系统要 创建主存两倍大小的页式虚拟内存,页面大小 为 2 KB , 请问需要多少页? 15. 在分时/多任务系统里,如果两个进程同时访问 同一个文件,会发生怎样混乱的情况?是否存 在文件管理程序应批准这种请求的情形?是否 存在文件管理程序应拒绝这种请求的情形? 16. 应用软件和系统软件之间的区别是什么?请 各举一个例子。 17. 定义多处理器体系结构情况下的负载平衡与 均分。 18. 概述引导过程。 19. 为什么说引导过程是必要的? 20. 如果你有一台 PC , 记录开机时你所观察到的 活动序列。然后确定在引导进程实际开始工作 之前有哪些信息显示在计算机屏幕上?什么 软件写下这些信息? 21. 假定多道程序设计操作系统分配的时间片是 10毫秒,计算机每纳秒平均执行5条指令,那 么在一个时间片内能执行多少条指令? 22. 如果一个打字员每分钟能打60个单词(在这里 假设一个单词含5个字符),问每打一个字符 要多久?如果多道程序设计系统分配的时间 片为 10 ms , 我们忽略进程间切换的时间,问 打一个字符要分配多少时间片? 23. 假定一个多道程序设计系统分配时间片为 50 ms , 如果把磁盘的读写磁头定位到所希望 的磁道上通常要花费 8 ms , 并且磁道上所要的 数据旋转到读写磁头之下通常要17 ms , 那么 等待一个读磁盘操作发生可能要多少个时间 片?如果该机器每纳秒能执行10条指令,那么 在这个等待时间里可以执行多少条指令?(这 就是为什么当一个进程用外围设备完成操作 时,多道程序设计系统终止这个进程的时间 片,让另一个进程运行而让第一个进程等待外 围设备的服务。) 24. 列举一个多任务操作系统必须协调访问的 5 种 资源。 25. 如果一个进程需要执行大量的 I/O 运算则称为 I / O 密集型进程,而如果一个进程由大多数在 CPU / 内存中完成的运算构成则称为计算密集 型进程。如果这两种进程都在等待分配时间 片,请问如何确定它们的优先级?为什么? 26. 在多道程序设计系统里运行两个进程,如果 它们两个都是 I / O 密集型进程,或者一个是 I/O 密集型进程另一个是计算密集型进程(如上 题所述),那么它们是否能达到较大吞吐量? 为什么? 27. 编写一组指令告诉操作系统的分派程序,在一 个时间片用完肘该做什么? 28. 在进程状态中包含什么信息? 29. 列出多道程序设计系统中一个进程不会全部 用完分配给它的时间片的情况。 30. 按照时间顺序列出一个进程被中断时发生的 主要事件。 31. 按照你所使用的操作系统回答下列问题。 a . 如何请求操作系统把一个文件从一个地方 复制到另一个地方? b . 如何请求操作系统显示磁盘上的目录? c . 如何请求操作系统执行一个程序? 32. 按照你所使用的操作系统回答下列问题。 a . 操作系统如何限制仅允许已批准用户访问 资源? b . 如何让操作系统显示当前在进程表里的 进程? c . 如何告诉操作系统你不想该机器的其他用 户访问你的文件? *33. 解释许多机器语言里“测试-置位”指令的重 要用法。为什么整个测试-置位过程作为单个 指令实现是重要的? *34. —个银行家只有 100 000 美元,贷款给两个客 户,每位 50 000 美元。后来这两位客户回了同 样 的话: 他们在能够还贷之前各自还需10 000 美元,以完成与先前贷款有关的商业交易 D 这 个银行家通过从其他地方借来资金来追加给 这两个客户的贷款(提高贷款利率)解决了这 个死锁问题。在死锁的三个条件中,银行家消 除了其中的哪个条件? *35. 每个想参加本地大学的铁路修建模型 n 课程 的学生,都要得到教师的允许,并且交纳实验 费。这两个要求可以在校园的不同地点办理, 复习题 97 可以按照任意顺序独立完成。注册学生限制为 20名;这个限制由教师和财务处一起掌握,前 者只授权20名学生,后者只收20名学生的费 用。假定这个注册系统有19名学生成功注册了 这一课程,但是最后这个名额有两名学生竞 争——一名仅得到了老师的允许,另一名仅交 纳了费用。下面解决该问题的各个方案中,分 别消除了死锁的3个条件中的哪个? a . 同意这两名学生都参加该课程。 b . 该班人数降为19人,因此这两名学生都不 能注册该课程。 c . 拒绝这两名竞争的学生,让第三名学生作 为第20名。 d . 注册该课程的要求改为 一个: 交纳费用。于 是交了费用的学生注册成功,另一名被拒绝。 *36. 由于计算机显示屏每块区域一次只能被一个 进程使用(否则屏幕中的图像将难以认清), 这些由窗口管理程序分配的区域是不可共享 的。为了避免死锁,窗口管理程序应消除死锁 的3个必要条件中的哪一个? *37. 假设一个计算机系统里不可共享资源分为3 类 : 1级,2级,3级。其次,假设系统中的每 一个进程都要根据这个类别请求它所需要的 资源。也就是说,它请求2级资源之前必须一 次请求所有必要的1级资源。一旦它得到了 1 级资源,就可以申请所有必要的2级资源,依 次类推。这个系统会出现死锁吗?为什么? *38. 机器人的两个手臂是程序控制的,它们从传送 带上取零部件,测试它们的公差并根据结果分 别把它们放到两个箱子中。零部件一次到达一 个,它们之间有足够的 距离。 为了防止两个手 臂尝试抓同一个零部件,控制手臂的计算机共 享一个公共的存储单元。如果一个手臂在一个 零部件到来时是可用的,那么控制它的计算机 就读公共单元的值。如果该值非0,那么这一 手臂 让那个 零部件通过,否则,起控制作用的 计算机把一个非0的值放到这个存储单元,指 挥那个手臂抓起该零件,动作完成后再把值0 存入该存储单元。什么样的事件序列可能导致 两个手臂之间激烈竞争? *39. 说明队列在假脱机输出到打印机的过程中的 使用。 *40. 如果一个等待时间片的进程一直都没有获得 时间片,这称为 饥饿 ( starvation ) 。 a . 对于竞相通过十字路口的汽车来说,十字 路口的地面是不可共享的资源。控制这个 资源分配的是红绿灯,不是操作系统。如 果这个灯能够感知来自每个方向的交通流 量,并通过程序给较大流量的方向以绿灯, 流量少的方向就可能饥饿。请问“饥饿” 现象怎么避免? b . 在一个进程优先级保持固定的优先级系统 中,如果调度程序总是按优先级分配时间 片,那么在什么时候一个进程会感到“饥 饿”?(提示:相对于正在等待的进程来 说,刚执行完时间片的进程的优先级是多 少,并且接下来按哪种规则分配下一个时 间片?)你能猜到各种操作系统是怎么避 免这个问题的吗? Ml . 死锁和饥饿(参见习题 40) 的相似之处是什 么?差别又是什么? * 42 . 下面是“哲学家进餐”问题,它最初是由狄杰 斯特拉提出的,现在已经是计算机科学传说中 的一部分。 5 个哲学家围着一个圆桌就座,每个人面前放 了一盘细面条。桌上有 5 把叉子,每个盘之间 有一把,每个哲学家都在思考和吃面之间轮 换。为了吃面,一个哲学家需要拥有紧挨他盘 子的2把叉子。 说明“哲学家进餐”问题中的死锁和饥饿(参 见习题 40) 问题。 *43. 对于一个多道程序设计系统中的时间片,如果 使其越来越短,那么会发生什么情况?越来越 长呢? *44. 随着计算机科学的发展,机器语言已被扩展以 提供专门指令。在 3.4 节中介绍了这样3条在操 作系统中广泛使用的指令。这些指令是什么? 45. 列举操作系统管理员能执行而一般用户不能 执行的两个活动。 46. 操作系统如何防止一个进程访问另一个进程 的存储空间? 47. 假定一个口令由9个取自英文字母表 (26 个字 符)的字符组成。如果测试每个可能的口令需 要1毫秒,那么测试所有可能的口令需要多长 时间? 48. 为什么为多任务操作系统设计的各个 CPU 能 够在不同特权级运行? 49. 列出两个由有特权的指令请求的典3^活 动? 50. 列举一个进程可能挑战计算机系统(如果未被 操作系统防止这样做)安全性的3种方式。 98 第 3 章操作系统 51. 什么是多核操作系统? 54. 因特网浏览器是微软的 Windows 操作系统的一 52. 固件更新和操作系统更新之间的区别是什么? 部 分吗? 53. 窗口管理程序与操作系统有什么关系? 55. 嵌入式操作系统能够解决哪些特殊问题? 丰 i 会尚— ____ 下面的问题有助于分析一些与计算领域相关的伦理、社会和法律问题。回答这些问题不是 唯一的目的,还应该考虑为什么这样回答,以及你的判断是否对每个问题都标准如一。 1. 假定你在使用一个多用户操作系统,它允许你查看其他用户的文件的名字,而且如果其 他用户的文件没有加保护措施,它还允许查看那些文件的内容。未经允许就查看这些信 息类似于未经允许就闲逛别人未锁门的房间,还是类似于阅读放在公共休息室(如医生 的候诊室)的资料? 2. 若访问一个多用户计算机系统,你在选择口令时有什么责任? 3. 如果一个操作系统有安全性缺陷,使得一个恶意的程序员能够在未经批准的情况下访问 其敏感数据,那么该操作系统的开发人员应该负多大的责任? 4. 你有责任锁好门防止入侵者入内,还是公众有责任在未受邀请时待在门外?操作系统有 责任防备别人对计算机及其内容的访问,还是非法入侵者有责任不入侵计算机? 5. 在《瓦尔登湖》一书中,梭罗坚持认为,我们己经变成自己工具的工具。也就是说,我们 并非从所拥有的工具中受益,而是要花费时间得到工具和维护工具。对于计算机,这在 多大程度上是真的?如果你有一台个人计算机,那么你花多少时间去赚钱承担它的费用、 学习如何使用它的操作系统、学习如何使用它的实用程序和应用软件、维护它,以及为 它的软件下载更新包?你得到的好处的时间量与你花费的时间总量相比又如何?使用它 时,你花费的时间值得吗?有或没有个人计算机会对你的人际交往活跃度有影响吗? 课外阅读 Bishop, M. Introduction to Computer Security. Boston, MA: Addison-Wesley , 2005. Davis , W. S. and T. M Rajkumar. Operating Systems: A Systematic View, 6th ed. Boston , MA: Addison- Wesley, 2005. Deitel, H. M., P. J. Deitel, and D. R. Chof&ies. Operating Systems^ 3rd ed. Upper Saddle River, NJ: Prentice-Hall, 2005. Nutt, G. Operating Systems'. A Modern Approach, 3rd ed. Boston, MA: Addison-Wesley, 2005. Rosenoer, J. Cyberlaw, The Law of the Internet. New York: Springer, 1997. Silberschatz, A., P. B, Galvin, and G, Gagne. Operating System Concepts, 8th ed. New York: Wiley, 2008. Stallings,W. Operating Systems, 5th ed. Upper Saddle River, NJ: Prentice-Hall, 2006, Tanenbaum, A. S. Modem Operating Systems, 3rd ed. Upper Saddle River, NJ: Prentice-Hall, 2008. 组网及因特网 t 章讨论计算机科学中称为组网的领域,包括学习如何将计算机连接起来共享信息和资 源。学习的内容包括网络的结构与操作、网络的应用以及网络安全问题。学习的一个 重点主题是遍布世界范围的特殊网络——因特网。 44 肖秦基織 *4.4 因特网姑议 ----- : . •. v 丄 , V. ,:h . , •••:: . , i; . 4.5 幸全性= 复习题 社会问题- 谭外阋读 人们对不同计算机之间共享信息和资源的需求催生了相互连接的计算机系统,它被称为网 络 ( network )。 计算机通过网络连接在一起,数据可以从一台计算机传输到另一台计算机。在 网络中,计算机用户可以相互交换信息,并且可以共享分布在整个网络系统中的资源,如打印 功能、软件包以及数据存储设备。用于支持这类应用的基础软件也已经从简单的实用软件包升 级为扩展网络软件系统,从而可以提供一个复杂的网络范围的基础架构。从某种意义上说,网 络软件正发展成为网络范围的操作系统。本章将探讨计算机科学中这个不断发展的领域。 4.1 网络基础 _ 我们从多种基本的网络概念开始学习网络。 4.1.1 网络分类 计算机网络通常分为 LAN ( Local Area Network , 局域网)、 MAN (Metropolitan Area Network , 城域网)和 WAN (Wide Area Network , 广域网)。局域网通常由一幢建筑物或者综合建筑楼群 中的若干计算机组成。例如,大学校园的计算机或者工厂中的计算机都可以用局域网连接。城 域网属于中型网络,例如可以覆盖某一社区。广域网连接的计算机覆盖范围更广,可以是相邻 的城市,也可以是在世界的两端。 网络的另一种分类依据是,网络的内部操作是基于公共领域的设计,还是基于特定实体(如 个人或公司)所拥有并控制的创新。前一种类型的网络称为 开放式 ( open ) 网络,后一种称为 封■闭式 ( closed ) 网络,有时也称为专用 ( proprietary ) 网络。开放式网络允许自由流通,因此 更容易被大众所接受,这就是它们通常最终战胜专有网络的原因,因为专有网络的应用受到许 可费和合约条件的限制。 100 第 4 章组网及因特网 因特网(马上要讲到的一种全球流行的网络的网络)属于开放式系统。尤其是,贯穿因特 网的通信是由一组称为 TCP/IP 协议簇的幵放标准控制的 C 见 4.4 节)。任何人都可以自由地使用 这些标准,而不需要付费或是签署许可协议。相反,像 Novell 这样的公司可能开发一些存在所 有权的系统,并通过出售或出租它们获利。 网络的另一种分类方法依据的是网络拓扑学,网络拓扑是指计算机的连接模式。众多拓扑 中,比较常见的两种类 型是: 总线型拓扑,即所有计算机都通过同一条被称为“总线”的通信 线路连接起来(如图 4- la ); 星型拓扑,即将一台计算机作为中心,所有其他计算都与之相连 (如图 4- lb )。 20 世纪 90 年代,总线型拓扑得以流行,当时是通过称为以太网的一组标准实现的, 而且以太网依然是目前使用最广泛的组网系统之一。星型拓扑可以追溯到 20 世纪 70 年代,它由 一台大型中央计算机服务于多个用户的范例发展而来。随着用户使用的简单终端发展成为小型 计算机,星型拓扑应运而生。目前,星型拓扑配置在无线网络中应用比较广泛,无线网络利用 无线电广播和中央计算机实现通信,这里的中央计算机称为 AP (Access Point , 接入点),是协 调所有通信的焦点。 . 总线型拓扑 计算机,计算机 计算机 计箅机 计算机 b . 星型拓扑 计算机~计龜 〆 韻机 计算机 计算机 图 4-1 两种常见的网络拓扑结构 - 以太网是^组: 标凑,,遵 平实典 利專捧 ■麵_緯_醜馬減啤。:它的 專字 遽:最趣姨破 矣网 设坪,其中的计算机由祢为以太的.同轴电璣连:編 >义;网在篇 ? o 赛我发; m 與已 鉉由 IEEE 标准化,成为,1辟尽802标准体系的一部分。以太网是个人计^机磁网的養 if 用方法。 的确, 用于 PC 的以太网卡现 :已成 为标准组件,可在目前的零售市场上买辨二:, " ,,,, 「事实上,今天有很多版务的以太网 7 反映了技米的进步以及更高的複输道率。 1 然而,、各 种版本都具有以太家裱的公美特性,其中包括将传输用的数据打包的 格式、实际择输的 二进 制往曼彻斯特编码 (表示 0和1的一种方法,0表杀为向下跳变的信夸,1:表示为.向 i 跳变的信 号)的使用,以及使用 c : siviAycD 控制传输的权限。 … : 总线型网络和星型网络在计算机物理排列上的区别并非总是很明显。二者的区别在于网络 中的计算机是通过一条公共总线直接互相通信,还是通过中央计算机媒介间接通信。例如,总 线型网络可能不会出现一条长总线且与计算机的连线却很短的情况,如图 4-1 所示。相反,总线 型网络可能拥有一条非常短的总线,而与每台计算机的连线却很长,这意味着总线型网络看起 来会比较像星型网络。确实,有时需要将每台计算机与中央位置通过网线连接,而在中央位置 又把它们连接到一种叫做 集线器 Omb ) 的设备,从而构成总线网络。集线器其实就是一条非常 短的总线,其功能在于将接收到的任何信号(可能会经过一些放大)传回给与之相连的所有计 算机。尽管其结果看起来像星型网络,但操作上像总线型网络。 4.1.2 协议 为了网络运行可靠,必须建立管理网络活动的规则,这类规则 称为协议 ( protocol )。 通过 4.1 网络基础 101 开发及采纳协议标准,商家生产的网络应用产品则可以与其他商家的产品兼容。因此,在网络 技术的开发中,协议标准的开发是一个必不可少的环节。 为了解协议概念,我们考虑这样一个问题,如何协调网络中计算机之间报文的传输。如果 没有控制此类通信的规则,所有的计算机就很可能同时抢着传输报文,或者在其他机器需要协 助时也无法提供帮助。 在基于以太网标准的总线型网络中,报文传输的许可是通过名为 CSMA/CD (Carrier Sense , Multi-Access with Collision Detection , 带冲突检测的载波侦听多路访问)的网络协议控制的。该 网络协议规定每条报文都要广播给总线上的所有计算机(如图 4-2 所示)。每台计算机都对所有 报文进行监听,但是只保存发送给自己的报文。为了传输报文,计算机需要等到总线处于空闲 状态,然后才幵始传输报文并继续监听总线。如果另一台计算机也开始传输报文,那么两台计 算机都会检测到这种冲突,并各自暂停一段随机长的时间,然后再次尝试传输。这种结果就像 一 小组人在交谈中使用的次序一样,如果两个人同时开始讲话,他们两个都会停下来。不同的是, 人们可能会有一系列对话 ,如: “对不起,刚才你想说什么? ”“不,不,你先说。”但是在 CSMA/CD 网络协议下,每台计算机都只会稍后再作尝试。 计算_ 计算棋 …计算机 图 4-2 总线型网络的通 { 目 注意, CSMA / CD 和无线星型网络并不兼容。在无线星型网络中,所有的计算机都通过中央 接入点通信,原因在于一台计算机可能无法检测到与其他计算机的传输冲突。例如,一台计算 机可能监听不到其他计算机,因为自己的信号淹没了其他计算机的信号。另一个原因可能是不 同计算机传输的信号由于障碍物或者距离的原因互相阻塞,虽然它们都能与中央接入点通信, 这种情况称为 隐藏终端问题 (hidden terminal problem ) ,如图 4-3 所示。这使得无线网络采用避 免传输冲突的方法,而不是检测冲突的方法。这种方法被归类为 CSMA/CA (Carrier Sense , Multiple Access with Collision Avoidance , 带冲突避免的载波侦听多路访问),其中很多方法是由 IEEE (参见第7章中的“美国电气及电子工程师协会”)在 IEEE 802.11 中定义的协议下进行标准 化的,通常被称为 WiFi (无线保真)。需要强调的是,冲突避免协议的设计目的是避免冲突,也 许并不能完全消除冲突。当冲突发生时,必须重新传输消息。 冲突避免最常见的方法是将优先权赋予已经在等待传输机会的计算机。这种协议和以太网 的 CSMA / CD 有相似之处。二者的主要区别在于当一台计算机首先需要传输报文,并且发现信道 处于空闲状态时,它并不是立即开始传输。相反,它会等待短暂的时间,只有当信道在这一段 时间内都保持在空闲状态时才会开始传输。如果在这个过程中,遇到信道被占用的情况,那么 计算机就会等待一段时间,时间的长度随机决定,然后再重新尝试传输。一旦这段时间耗尽, 计算机就被允许立即占用空闲的信道。这就意味着避免了 “新来者”和已经处于等待状态的计 算机之间的冲突,因为“新来者”需要等到一直处于等待状态的计算机开始传输之后,才会被 允许占用空闲的信道。 102 第 4 章组网及因特网 图 4-3 隐藏终端问题 然而,该协议无法解决隐藏终端问题。毕竟,任何基于辨别空闲或者繁忙信道的协议都需 要每个站点能够监听到其他所有站点。为了解决这一问题,一些 WiFi 网络要求每台计算机向接 入点发送简短的“请求”报文,并等待接入点确认收到请求,然后再传输完整的报文。如果接 入点由于正在处理“隐藏终端”处于繁忙状态,将会忽略请求,然后请求的计算机将获悉并等 待。否则,接入点会确认请求,并且计算机将获悉现在进行传输是安全的。注意,尽管计算机 无法监听到正在发生的报文传输,但是网络中所有的计算机都能监听到接入点发出的确认,因 此就能知道接入点在任何特定的时间是否繁忙。 4.1.3 网络互连 有时候需要连接现存的网络以形成一个扩展的通信系统,形成相同类型的更大的网络。例 如,对于基于以太网协议的总线型网络,经常可以将总线连接起来以形成一个长总线。它是利 用中继器、网桥、交换机等不同的设备完成的,这些设备区别微妙且信息量大。最简单的是中 继器 ( repeater ), 它仅仅是奄两个原始总线间简单地来回传送信号(通常有某种形式的放大) 的设备,而不会考虑信号的含义(如图 4-4 a 所示)。 网桥 ( bridge ) 类似于中继器,但是更复杂一些。它也是连接两条总线,但是不必在线路上 传输所有的报文。相反,网桥要检查每条报文的目的地址,并且当该报文的目的地是另一边的 计算机时才将其在线路上传输。因此,在网桥同一侧的两台计算机不需要打扰另一边的通信就 可以互相传输报文。相对于中继器,网桥形成的系统更加髙效。 交换机 ( switch ) 本质上就是具有多连接的网桥,可以连接若干条总线,不止两条。因 此,交换机形成的网络包括若干从交换机延伸出来的总线,它们就类似于车轮的辐条(图 4-4 b ) o 与网桥一样,交换机也要考虑所有报文的目的地址,并且仅仅转发那些目的地是其 他“辐条”的报文。此外,被转发的报文只会送至相应的“辐条”,因此减轻了每根“辐条” 的传输流量。 4.1 网络基础 103 需要注意的是,当计算机通过中继器、网桥以及交换机连接时得到的是一个大网络。整个 系统用相同的方式运作(使用相同的协议),就像每个规模较小的初始网络一样。 然而,要连接的网络有时候会有不兼容的特性。例如, WiF 剩络的特性就可能与以太网网 络中的不兼容。在这种情况下,网络必须按建立一个网络的网络[称 为互联网‘ ( internet )] 方式 连接。在这个网络中,原始的网络仍然保持其独立性,并且继续作为独立的网络运行。(注意, 普通名词互联网 ( internet ) 不同于因特网 ( Internet )。 后者首字母要大写,指的是一种独特的、 世界范围的互联网,在本章的其他节会学到。现实中有许多互联网的例子。事实上,在因特网 流行之前,传统的电话通信就是由世界范围的互联网系统操控的。) 把网络连结起来形成互联网的设备是 路由器 ( router ), 这是一种用来传送报的专用计算 机。注意,路由器的任务与中继器、网桥和交换机的不同,路由器提供了网络之间的链接,并 允许每个网络保持它独特的内部特性。作为一个例子,图 4-5 描述了通过路由器组连接两个 WiFi 星型网络和一个以太网总线网络的情形。当某个 WiFi 网络中的一台计算机想要给以太网中的一 台计算机发送报文时,它首先会把报文发送到其网络中的接入点,接入点再把报文发送到与之 相连的路由器,该路由器把报文转发至以太网中的路由器。在那里该报文被发送给总线上的一 台计算机,然后这台计算机把报文转发到它在以太网中的最终目的地。 图 4-5 路由器连结了两个 WiFi 网络和一个以太网络,形成了一个互联网 104 第 4 章组网及因特网 _ 路由器得名的原因在于它的用途是向适当的方向转发报文。这个转发过程基于互联网范围 的寻址系统,其中互联网上的所有设备(包括原始的网络中的计算机和路由器)都被赋予了唯 一的地址。(这样,原始网络中的每台计算机都有两个 地址: 自己网络内的“本地”地址和它的 互联网地址。)一台计算机想给远程网络中的另一台计算机发送报文,就要附上报文的目的地的 互联网地址,然后把报文发送给其本地的路由器,在那里报文将向正确的方向转发。为了转发 报文,每个路由器都维护了一张 转发表 (forwarding table ), 该表中包含了根据目的地地址消息 应该发送的方向这种路由信息。 一个网络与互联网链接的“点”经常称 为网关 ( gateway ), 因为它是网络与外部世界之间 的通道。网关有多种形式,因而这个术语的使用不太严谨。在许多情况下,网络的网关仅仅是 路由器,通过它网络能与互联网上的其他网络通信。在其他情况下,术语网关所指的可能不仅 仅是一台路由器。例如,在连接到因特网的多数住宅 WiFi 网络中,术语网关指的是网络的接入 点和与接入点相连的路由器,因为这两个设备通常安装在一个单元中。 4.1.4 进程间通信的方法 一个网络中,在不同计算机上执行(甚至在一台计算机上通过分时/多任务处理执行)的各 种活动(或进程)必须经常互相通信,以便协调行动并完成指派的任务。这种进程之间的通信 称为 进程间通信 (interprocess communication ) 。 进程间通信通常采用的是客户机/服务器 ( client / server ) 模型。这种模型规定了进程的基本角 色,或者是向其他进程发出请求的客户机 ( client ), 或者是满足客户机请求的服务器 ( server )。 客户机/服务器模型最初应用于连接办公室间的所有计算机的网络。这样,网络中的所有计算 机都可以使用连接到该网络上的性能良好的唯一打印机,在这种情况下,打印机的角色就是服务 器(常称为 打印服务器, print server ), 其他计算机则为客户机,传递打印请求给打印服务器。 客户机/服务器模型的另外一种早期应用,用于减少磁盘存储开销以及复制记录的需要。在 这种情况下,网络中的某一台计算机需要配置高容量海量存储系统(通常是磁盘),存储某一组 织的所有记录,那么网络中其他计算机就可以在需要这些记录时提出请求。于是,包含记录的 计算机的角色就是服务器,称 为文件服务器 (file server ), 而那些向文件服务器提出存取请求的 计算机就扮演客户机的角色。 客户机/服务器模型在当今的网络应用中使用广泛,在本章后面几节会介绍这一点。不过, 客户机/服务器模型不是进程间通信的唯一方式,另外一种 是对等 ( peer - to - peer , 通常简称为 P 2 P ) 模型。客户机/服务器模型为一个进程(服务器)与另外多个进程(客户机)通信,而对等模型 为两个进程间对等通信 (见图 4-6)。此外,服务器必须持续运作,以便随时服务于客户机,但 是对等模型涉及的则是临时执行的进程。例如,对等模型的应用包括发送即时消息(其中人们 通过因特网进行文字交流),也可以是人们玩交互式竞技游戏。 对等模型也是分发文件(如因特网上的音乐和电影)的常用方法。在这种情况下,一个对 等体可以从另一个对等体接收文件,然后把此文件提供给其他的对等体。以这种方式参与分发 的对等体集合有时称为蜂群 ( swarm ) 0 文件分发的蜂群方法与先前使用客户机/服务器模型的方 法相反,读模型需要建立中央分发中心(服务器),以使客户端从该中心下载文件(或至少是找 到那些文件的源)。 从文件共享的角度来看, P 2 P 模型正在取代客户机/服务器模型的一个原因在于它把服务分 布到许多的对等体上,而不是集中在一个服务器上。这种非集中化的操作构建了更高效的系统。 遗憾的是,基于 P 2 P 模型的文件分发系统流行的另一个原因在于(假设合法性遭到质疑)缺乏 4.1 网络基础 105 中心服务器使得版权执法行动变得更为困难。但是,也存在许多案例,其中一些人发现“困难” 并不意味着“不可能”,并且由于对版权的侵犯,他们发现自己面临着重大的责任。 服务器 (a ) 服务器在任何时候都必须准备好为多个客户机服务 对等体 4- ^对等体 (b ) 两个进程在一对一的基础上平等地通信 图 4-6 客户机/服务器模型与对等模型的比较 你可能经常读到或者听到“对等网络”这个说法,这个例子正好说明了,非科技界釆用科 技术语时是如何产生误用的。“对等”指的是两个进程通过网络(或者互联网)通信的一种系统, 并不是网络(或者互联网)的一种特性。一个进程可以幵始采用对等模型与另外一个进程通信, 接着又采用客户机/服务器模型通过同一个网络与另外的进程通信。因此,精确地说应该是利用 对等模型通信,而不是通过对等网络通信。 4.1.5 分布式系统 因为组‘网技术的成功,计算机之间通过网络的交互己经很普遍,并且涉及方方面面。许多 现代软件系统,例如全球信息检索系统、公司范围的会计和库存系统、计算机游戏甚至操控网 络基础设施本身的软件,都被设计成分布 式系统 (distributed system )。 这意味着,它们由在网 络中不同计算机上作为进程执行的软件单元组成。 分布式系统最开始是独立开发的。不过,今天的研究已经发现在这些系统之间可以使用公 共的基础设施,例如通信系统以及安全系统。于是,人们开始努力生产能够提供这种基础设施 的预制系统。因此要构建一个分布式应用,只需要幵发系统中应用所特有的部分即可。 现在,一些类型的分布式计算系统已经很常见。 集群计算 (cluster computing ) 指的是一种 分布式系统,其中多个独立的计算机密切合作,提供的计算和服务可与大得多的机器相比拟。 这些独立的机器,加上连接它们的高速网络的总成本,要比一台高价位的超级计算机的成本低, 但其可靠性更高,且维护成本更低。这样的分布式系统用于提供高 可用性 ( high - availability ,因 为这更有可能保证集群中至少有一台计算机可以响应请求,即使集群中其他计算机发生故障或 不可用) 和负载平衡 ( load - balancing , 因为负载可以自动从集群中负载太大的计算机转移到负 载很小的计算机上)。 网格计算 (grid computing ) 是指与集群相比稱合度不高的分布式系统, 但其成员机器仍共同协作来完成大型任务。网格计算可能需要专门的软件,以便更容易地发布 数据和算法到网格中的计算机。烕斯康星大学的 Condor 系统和 BOINC ( Berkeley’s Open Infrastructure for Network Computing , 伯克利开放式网络计算平台)都属于这种类型。这两种系 •统通常安装在用于其他目的的计算机上(如办公用 PC 和家用 PC ), 当机器空闲时便可为网格自 愿提供计算能力。由于因特网日益增强的连通性,即这种自愿行动,分布式网格计算使得数以 百万计的家用 PC 都可以解决无比复杂的数学问题和科学问题。凭借 云计算 (cloud computing ), 106 第 4 章组网及因特网 网络上大量的共享计算机得以被按需分配给客户机使用。云计算是分布式系统中的最新趋势。 20世纪初大城市电网的发展使得个体工厂和企业不再需要维护自己的发电机,与此非常类似, 因特网使得实体可以将其数据和计算委托给“云”,这里的“云”指的是网络上可用的大量计算 资源。例如,亚马逊的弹性计算云 (Elastic Compute Cloud ) 服务允许客户按小时租用虚拟计算 机,而不需要考虑计算机硬件的实际位置。另夕卜, Google Docs 和 Google Apps 允许用户在信息方 面进行协作,允许他们在构建 Web 服务时不需要了解多少台计算机在用于求解同一问题或相关 数据存储在何处。云计算服务就可靠性和可扩展性提供了合理保证,但却引发了人们对于隐私 和安全性的担忧,因为我们也许再无法知晓谁在拥有着、操作着我们所用的计算机。 1 hi f.i-n. : ; . ;•' •- '•- . : r : .. 1. fr 么是开放式网络? 2. 归纳网桥和交换机之间的 K 别。 3. :# 么是路由器? 4二列举社余中一些符 合客户机人 艇务器 模犁的 关系。, ::雞' __綱■觀酵賴難 _彎_ 獄 IP:” ㈤ ■ 麵 : r ” 6 .请棺^集群计算和两格计算之间的区别 。 4.2 因特网 互联网中最著名的例子就是 因特网 ( Internet , 注意首字母是大写的),它起源于20世纪60 年代的研究项目。它的目的是开发出将许多计算机网络链接起来的能力,以便它们能作为一个 连接的系统发挥作用,而这个系统不会由于局部灾难而瓦解。这个项目的工作绝大部分是由美 国政府资助并通过 DARPA (Defense Advanced Research Projects Agency , 美国国防部高级研究计 划署)发起的。经过这么多年,因特网的开发已经从政府资助的项目转变成了学术研究项目, 而且如今在很大程度上,它已经是商业项目了,用于连接全世界局域网、城域网及广域网,涉及 上百万台计算机。 :' :媚•爾爾 ㈣ :呤 硪:% 錶■^士轉,常产毐,: 1 科轉界乎_矜%稀兔華二戎因#掏 ( 目 七们初算_寒位卞#术系鍊:,涉#多与工业及政府有合作关系的 大聲 。目& ^£_高_政因_应用淨进行料研,包括远^■问及控制昂責的最新型 设务,例^^#與_斷仪咸^规泰藏一个_例 子是: 由人实施远程碎科手术, ,麵史爹:癖#雜掄 捧繭# 情-息 r :. ,二 : ■' 1 .' 1 4.2.1 因特网体系结构 我们已经提到,因特网是相连网络的集合 D 总体上,这些网络的构建和维护是由 ISP (Internet Service Provider , 因特网服务提供商)来完成的。就网络本身而言,通常也习惯使用 术语 ISP 来表示。因此,当说到要连接到一个 ISP 时,我们真正的意思是连接到由 ISP 所提供的 网络。 由 ISP 运作的网络系统可以按照各网络在整个因特网结构中所起的作用分类成层次结构 (如 4.2 因特网 107 图 4-7 所示)。整个层次结构的顶部是数量相对较少的 第一层 ISP ( tier -1 ISP ), 这些 ISP 拥有非常 高速、髙容量的国际化广域网。这些网络被看成是因特网的主干,它们通常是由通信行业中的 大公司来运作的。例如,有一家公司最初是传统的电话公司,后来它们把服务领域扩展到提供 其他通信服务。 h 第一层 isp 一第二层 ISP 因特网接入 服务提供商 1终端系统 图 4-7 因特网的构成 与第一层 ISP 连接的是 第二层 ISP ( tier -2 ISP ), 第二层因特网服务提供商往往是区域性的, 在实力上也稍逊一些。(第一层和第二层因特网服务提供商的区别通常是仁者见仁,智者见智。) 此外,这些网络通常也是由通信行业的公司运营。 第一层和第二层因特网服务提供商本质上是路由器的网络,集中提供因特网通信基础设施。 它们同样可以被认为是因特网的核心。通常由称为因 特网接入服务提供商 (access ISP ) 的中间 商提供与这一核心的接入服务。因特网接入服务提供商本质上是独立的互联网,有时也称为内 联网 ( intranet ), 由向个人用户提供因特网接入业务的机构来运营。这些公司包括 AOL 、 微软以 及本地通过提供服务收取服务费的有线电视和电话公司,另外还包括一些大学或者公司等组织, 它们为组织内部的个人用户提供因特网接入。 个人用户与因特网接入服务提供商连接的设备 称为终端系统 (end system ) 或 者主机 ( host )。 这些终端系统并不一定是传统意义上的计算机。它们包括很多种设备,如电话、摄像机、汽车 和家用电器。毕竟,因特网本质上是一个通信系统,因此任何可以与其他设备进行通信并从中 获益的设备都是潜在的终端系统。 终端系统与因特网接入服务提供商连接的技术也不尽相同。或许,发展最快的是基于 WiFi 技术的无线连接。策略是将接入点与因特网接入服务提供商相连接,因此可以在接入点的广播 范围内通过因特网接入服务提供商向终端系统提供因特网接入。接入点范围内的区域通常称为 热点 (hot spot )。 热点和热点组的范围日益广泛,包括个人住宅、酒店和写字楼、小型企业、 公园,甚至有些情况下是整个城市。目前手机行业也使用相似的技术,在手机行业中热点就是 我们所知的服务区,当终端系统从一个服务区移动到另一个服务区时,通过调整产生服务区的 “路由器”来提供连续的服务。 连接因特网接入服务提供商的其他常见技术使用电话线或者电缆/卫星系统。这些技术可以 用来提供对单个终端系统的直接连接,或者连接客户的路由器从而实现连接多个终端系统。后 108 第 4 章组网及因特网 一种方法在个人住宅中的应用日益流行,利用现有的电缆或者电话线,通过连接因特网接入服 务提供商的路由器/接入点形成本地热点。 现有的电缆和卫星链接与高速数据传输的兼容性天生要比与传统电话线的兼容性好,电话 线最初是基于语音通信安装的。但是,人们已经制定了一些更明智的方案,拓展了这些语音链 路,以适应数字数据的传输。这些连接使用了名为调制解调器 ( modem , modulator/demodulator 的简称)的装置,该装置将数字数据转换为与使用的传输媒介兼容的格式。例如 , DSL (Digital Subscriber Line , 数字用户线路)把低于4千赫兹的频率范围用来满足传统的语音通信,而将更 高的频率用来传输数字数据。此外,较为陈旧的方法是将数字数据转换为语音数据,然后使用 和语音传输一样的方式进行传送。后一种方法称为拨 号连接 ( dial-up access ), 事实上拨号连接 用于临时连接,用户使用传统方式拨通因特网接入服务提供商的路由器,然后将其电话与要使 用的终端系统连接。虽然拨号连接成本低并且使用广泛,但是数据传输速率较低,无法满足当 今需要实时视频通信和传输大量数据的因特网应用^因此,越来越多的家庭和小型企业通过宽 带技术连接他们的因特网接入服务提供商,这些宽带技术包括有线电视连接、专用电话数据线 路、卫星天线,甚至光纤电缆。 4.2.2 因特网编址 如 4.1 节所介绍的,一个因特网必须与一个互联网范围的编址系统相连接,这个系统将赋予 该系统中每个计算机唯一的标识地址。在因特网中,这些地址称为 IP 地址 OP address )。 ( IP 是 Internet Protocol 的简称,指的是因特网协议,我们在 4.4 节中会更多地了解这个术 语。) 最初,每 一个 IP 地址都是32位的位模式,但是为了提供更多的地址,人们现在正计划将其扩展到128位(参 见 4.4 节关于 IPv6 的详述 )。 ICANN (Internet Corporation for Assigned Names and Numbers, 因特 网名称与数字地址分配机构)向因特网服务提供商提供了大量连续数字的 IP 地址,因特网名称 与数字地址分配机构是一家非营利性的国际组织,致力于协调因特网的运营。然后,因特网服 务提供商就可以将他们被授权范围内的地址块中的 IP 地址分配给其管辖范围内的计算机。因此, 因特网上的计算机都被分配了唯一的 IP 地址。 IP 地址通常是采用点分 十进制记数法 (dotted decimal notation ) 书写的,其中地址的每个字 节用圆点分隔,每个字节用传统的十进制整数表示。例如,使用点分十进制记数法,位模式 5.2 将可以表示2字节位模式0000010100000010,其中包含字节00000101 (5 的表示),接下来是字节 00000010 (2 的表示 ); 而位模式 17.12.25 将可以表示3字节的位模式,其中包含字节00010001 ( 用 二进制记数法表示17),然后是字节00001100 (用二进制记数法表示12),最后是字节00011001 (用二进制记数法表示25)。总之,当用点分十进制记法表示时,32位的 IP 地址可以表示为 192.207.177.133. 用位模式表示的地址(即使采用点分十进制记数法压缩)难以帮助人们理解。基于这个原 因,因特网拥有另外一种编址系统,利用助记名称来标识计算机。该编址系统基于域 ( domain ) 的概念,可以认为是如大学、俱乐部、公司或者政府机构等单个机构操作的因特网的“区域”。 (此处区域一词加了引号,这是因为它可能不同于因特网的物理区域,稍后我们将看到这点。) 每一个域都必须在因特网名称与数字地址分配机构进行注册,这一过程由称为 注册商 ( registrar ) 的公司操作,注册商由因特网名称与数字地址分配机构指定。作为注册流程的一部分,助记 域名会分配给域,域名在因特网中是唯一的。域名通常是注册域的机构的描述,从而提高它们 对用户的实用性。 例如, Addison - Wesley 出版公司的域名是 aw . com 。 需要注意的是,点后面的后缀反映了域 的分类,在这个域名中,后缀 com 就表明了它是一家商业机构 ( commercial )。 这种后缀称为 TLD 4.2 因特网 109 ( Top - LevelDomain , 顶级域名)。顶级域名还有不少,例如, edx 康示教育系统, gov 表示政府机 构, org 表示非营利机构, museuno 表示博物馆, info 表示无限制使用, net 最初打算用于表不因特 网服务提供商,但是现在使用的范围更广一些。除了这些一般的 TLD 外,也有用于表示特定国 家的2字母 TLD (称为国 家代码顶级域名), 例如, au 表示澳大利亚, ca 表示加拿大。 一旦一个域的助记名被注册,注册该域名的机构可以在域内自由扩展名称,为个体项获取 助记标识符。例如, Addison - Wesley 出版公司内的某台计算机可以标识为 ssenterprise . aw . com 。 注 意,域名是向左扩展的,并用句点分开。在一些情况下,使用称 为子域 ( subdomain ) 的多个扩 展在域内组织名称。这些子域通常代表域管辖内不同的网络。例如,如果 Nowhere 大学被分配的 域名为 nowhereu . edu , 那么 Nowhere 大学的某台计算机的域名可以为 r 2 d 2 xompsc . nowhereu . edu , 其含义是顶级域名为 edu 、 域名为 nowhereu 、 子域名为 compsc 、 名为 r 2 d 2 的计算机。(我们需要 注意的是,在助记地址中使用的点分表示法与位模式形式的地址中使用的点分十进制记数法没 有关系。) 虽然助记地址对于人类来说比较方便,但是因特网中还是使用 IP 地址来传输消息。因此, 如果某人想要给远程计算机发送消息并通过助记地址来标识目的地,那么使用的软件必须能够 将地址转换成 IP 地址,然后再传输消息。这种转换可以通过 域名服务器 (name server ) 来完成, 其实它是可以向客户端提供地址解析服务的目录。这些域名服务器都共同作为因特网范围内的 目录系统,称为 DNS (Domain Name System , 域名系统)。使用域名系统进行解析的过程称为 域名系统查找 (DNS Lookup )。 因此,如果一台计算机可以通过助记域名连接,那么这个域名必须存在于域名系统内的域 名服务器上。在注册了域名的实体拥有资源的情况下,它可以在域内建立并维护包含所^■名称 的域名服务器。事实上,这就是域名系统最初构建所用的模式。每一个注册域都代表了本地机 构所运行因特网的物理区域,如公司、学校或者政府机构。实际上,这个机构就是一个因特网 接入服务提供商,通过与因特网连接的内联网向其成员提供因特网接入。作为该系统的一部分, 机构维护自己的域名服务器,为域中使用的所有名称提供解析服务。 目前,这种模式依然很常见。然而,许多个体或者小型机构想要建立展示在因特网上的域, 但却不想承担必要的支持资源。例如,一家本地国际象棋倶乐部想要在因特网上展示为 KiBgsandQueens . org , 但是俱乐部不想提供资源建立自己的网络、维护网络与因特网的连接,也 无意去实现自己的域名服务器。这种情况下,倶乐部可以和因特网服务提供商签订合同,使用 因特网服务提供商已经建好的资源实现注册域名的展示。俱乐部一般可能会通过因特网服务提 供商的帮助,注册由倶乐部选好的域名,并与因特网服务提供商签订合同将域名放入因特网服 务提供商的域名服务器。这就意味着所有关于新域名的域名系统查找都会指向因特网服务提供 商的域名服务器,从而获取正确的域名解析。通过这种方法,许多注册的域名就可以存在于一 个因特网服务提供商的域名服务器中,每个域名只占用一台计算机的很小一部分。 4.2.3 因特网应用 在这一小节中,我们从3项传统的应用开始论述因特网的一些应用。但是,这些“传统”的 应用不足以反映当今因特网的全貌。事实上,计算机和其他电子设备的区别己经变得含混不清。 电话、电视、音响系统、防盗自动警铃、微波炉和摄像机都是潜在的“因特网设备”。此外,因 特网的传统应用在新应用的大量扩展下相形见绌,新的应用包括即时消息、视频会议、因特网 电话和因特网广播。毕竟,因特网只是一个传输数据的通信系统。随着技术进步,系统的传输 速度不断提高,传输的数据内容就仅仅受到人们想象力的限制。因此,我们将介绍两种较新的 110 第 4 章组网及因特网 因特网应用(电话和广播),以展示与当今新兴因特网相关的问题,其中包括其他网络协议标准 的需求,因特网与其他通信系统链接的需求,以及扩展因特网路由器功能的需求。 1. 电子邮件 因特网最常见的用途之一就是电 子邮件 ( E - mail , 是 electronic mail 的缩写),电子邮件系统 可以在因特网用户之间传输信息。为了提供电子邮件服务,每个域的本地机构都要在其域内指 定一台计算机,用作该域 的邮件服务器 (mail server ) 0 通常,为了向域内的用户提供邮件服务, 因特网接入服务提供商都会在其域内建立电子邮件服务器。当一名用户在其本地计算机上发送 出电子邮件,邮件会首先传送到用户的邮件服务器上,然后邮件服务器会将邮件转发至目的地 邮件服务器上,目的地邮件服务器会一直保存邮件,等待收件人联系邮件服务器并请求查看收 到的邮件。 在邮件服务器之间传输邮件或者从作者的本地计算机向邮件服务器发送新消息,所使用的 网络协议是 SMTP (Simple Mail Transfer Protocol , 简单邮件传输协议)。简单邮件传输协议最初 是为传输 ASCII 编码的文本信息设计的,因此又开发了 MIME (Multipurpose Internet Mail Extensions , 多用途因特网邮件扩展)等协议,将非 ASCII 编码的数据转换成简单由 P 件传输协议兼容的格式。 有两种常见的协议可以用于访问到达和存储在用户邮件服务器的电子 邮件: POP3 (Post Office Protocol Version 3 ,邮局协议版本 3 ) 和 IMAP (Internet Mail Access Protocol , 因特网邮件 访问协议)。二者中,邮局协议版本3 ( POP 3, 读作 pop - three ) 较为简单。使用 POP 3,用户可以 向其本地计算机发送(下载)消息,在本地计算机中读取、在不同文件夹中存储,并可以随意 编辑或者操作那些消息。这些操作都是通过使用本地机器的大容量存储器在用户的本地机器上 完成的 。 IMAP (读作 EYE - map ) 支持用户在与邮件服务器相同的计算机上存储和操作消息以及 相关的资料。这样,必须在不同计算机上收取邮件的用户,就可以在邮件服务器上维护记录, 然后通过任何远程计算机来访问。 了解邮件服务器的作用后,我们就很容易明白单个邮件地址的结构了。它有一个符号串(有 时候称为账户名)表示某个人,然后是符号@ (读做 at ), 最后是助记串,表示接收该邮件的邮 件服务器。(事实上,这个串通常只表明目标域,该域的邮件服务器最终是要通过 DNS 查找来表 示 的。) 因此, Addison - Wesley 的某个人的邮件地址很可能是 shakespeare @ aw . com 的形式。换句 话说,一条发送到这个地址的报文将发送到域名为 awxom 的邮件服务器上,该报文一直保存在 该邮件服务器中,以便符号串为 Shakespeare 标识的人查看邮件。 2. 文件传输协议 传输文件(如文档、照片或者其他编码信息)的一种方法是将其作为附件附在电子邮件中。 不过一种更有效的方法是利用 FTP (File Transfer Protocol , 文件传输协议),它是一种在因特网 上传输文件的客户机/服务器协议。使用 FTP 传输文件,因特网中一台计算机的用户需要使用一 个实现 FTP 的软件包,然后与另外一台计算机建立连接。(最初的计算机相当于客户机,它所连 接的计算机相当于服务器,通常称为 FTP 服务器。)一旦建立了这个连接,文件就可以在两台计 算机之间以任意方向传输了。 FTP 已经成为因特网上提供受限数据访问的流行方式。例如,假设你打算允许一部分人检 索某文件,但是禁止其他人访问,于是,你仅仅需要用 FTP 服务器设备将该文件存入一台计算 机,然后通过口令限制对该文件的访问权限。然后,知道口令的人们就可以通过 FTP 访问该文 件,而其他人的访问就会被拒绝。在因特网中使用这种方式的计算机通常被称为 FTP 站点,因 为它成为因特网上可以通过 FTP 使用文件的地方。 FTP 站点也用于提供不受限的文件访问。为了实现这个目的, FTP 服务器要使用术语 4.2 因特网 111 anonymous (匿名)作为通用口令。这些地点经常被称为匿名 FTP (anonymous FTP ) 站点,并 因此提供不受限的文件访问权限。 虽然 FTP 客户机和服务器仍被广泛使用,但现在大部分用户发现他们的文件传输需要使用 HTTP 通过 Web 浏览器实现(将在下一节讨论)。 3. 远程登录与 SSH 因特网的一个早期应用是,允许计算机用户在很远的地方访问计算机。 远程登录 ( Telnet ) 就是为了这个目的建立的一个协议系统。使用远程登录,用户(运行远程登录客户端软件)可 以与远程计算机的远程登录服务器取得联系,然后遵循特定操作系统的登录步骤获取对那台远 程计算机的访问权。这样,通过远程登录,远程用户拥有与本地用户相同的对应用和工具的访 问权限。 由于它设计于因特网发展初期,远程登录有许多缺点。其中一个较为严重的是,通过远程 登录进行的通信是没有加密的。即使通信的主题不敏感,这个问题也很严重,因为用户的口令 也是登录过程中通信的一部分。因此,使用远程登录就可能给窃听者获取口令的机会,然后他 会滥用这一重要信息 。 SSH (Secure Shell , 安全 shell ) 是解决这个问题的一个远程登录候选方 法,而且正在迅速代替远程登录。 SSH 的特征是,它给传输中的数据提供加密以及验证 (4.5 节), 验证过程就是确定通信双方的身份。 4. VoIP 作为最近的因特网应用的一•个例子 , VoIP (Voice over Internet Protocol ) 利用因特网基础设 施提供与传统电话系统类似的语音通信。最简单的形式下, VoIP 由不同机器上的两个进程构成, 这些机器通过 PZP 模型传输音频数据——这个方法本身没有明显的问题。但是,初始化和接受呼 叫,把 VoIP 与传统电话系统对接,以及提供像紧急911通信这样的服务,诸如此类的问题都超出 了传统的因特网应用范畴。此外,拥有国家传统电话公司的政府把 VoIP 看成是一种威胁,对它 们征收很高的税,或彻底宣布它们为不合法。 现在有4种不同形式的 VoIP 系统在展开竞争。 VoIP $ 欠电话 ( softphone ) 通过 P 2 P 软件允许两 个或多个 PC 共享一个电话,所需硬件只是一个扬声器和一个麦克风。 VoIP 软电话系统的中的一 个例子就是 Skype , 它还为客户提供与传统电话通信系统的链接。 Skype 的一个缺点是它是一个 专有系统,因而许多操作结构是不对外公开的,这意味着 Skype 用户必须要在没有第三方论证的 基础上相信 Skype 软件的完整性。例如,为了接收呼叫, Skype 用户必须把 PC 与因特网相连,并 且 Skype 系统是可用的,这就意味着在 PC 机主毫无意识的情况下, PC 的一些资源可能会被用来 支持其他的 Skype 通信(这一功能引起了一些抵制)。 另一种形式的 VoIP 由模 拟电话适配器 (analog telephone adapter ) 组成。模拟电话适配器允 许用户将其传统电话连接到由某个因特网接入服务提供商提供的电话服务上。这一选择经常与 传统的因特网服务或数字电视服务捆绑。 第三种形式的 VoIP 是嵌入式 VoIP 电话。嵌入式 VoIP 电话把传统电话替换成了直接连接到 TCP/IP 网络上的等效的手持设备。嵌入式 VoIP 电话在大型组织中越来越常见,很多大型组织都 在将其传统的内部铜线电话系统替换为基于以太网的 VoIP , 以期通过这种方式来降低成本并增 强功能。 最后,下一代的智能手机将使用 VoIP 技术。也就是说,早期的几代无线电话仅使用特定公 司的协议与该电话公司的网络通信。通过公司的网络和因特网间的网关,我们便可以连接到因 特网,而信号会在那儿被转换为 TCP / IP 系统。然而,新兴的 4 G 电话网络被设计成完全基于 IP 的 网络,即 4 G 电话本质上只是全球因特网上的另一主机。 112 第 4 章组网及因特网 在过去的 10 年时间里,_移动电话技术已鐘从筒单的专用便:携承备发展成为象杂的多功能 手持式计算机。第一代界线 4 话网络通过空气件 轉^辦 语音停号,与传统的龟话系铳.非常相 似,只是没肴穿墙而过的铜藏」我如滅这些早期 U 4毐糸巍4知 1 “1 G ” 网 緣/即第一代 网络。 第二代网络使用教字信号对语音编码,能够更高效地使用.无线电波,还能够传输其他种类的 数字數織, #吝本,息。 , ,岸三代 ( 3 Q ) 电译0 聲拜 饿了 4 高 的数耨倩输涔率, n 支舞乎机视频 逋每&带备 #▲: 型活动。 4 G 岗络& 标包螓筻高: ^数 ㊆ 传输速率和一 V 彳吏射圯协议完全进 行分组交换的网络 ( 以这个网络为基础的最新一代智能手机将能够具有当前仅在基于宽带的 PC 本节 ■應; 5. 因特网广播 另一个最近的因特网应用是广播站节目的播送,这个过程称为网站广播,与原先的广播相 对应,因为信号是通过因特网传送的,而不是无线电传送。更准确地说,因特网广播是 音频流 (streaming audio ) 的一个具体示例,它是指在实时基准上传送声音数据。 从表面上看,因特网广播似乎不需要特殊的考虑,人们可能猜想到一个站点仅需建立一台 服务器,它把节目消息发送给请求它们的每个客户端。这种技术就是众所周知的多 点传播 ( N - unicast )。 (严格地说,单点传播是指一个发送者向一个接收者发送消息,而多点传播是指一 个发送者从事多个单点传播。)多点传播方法已经投入实际使用,但这种方法具有明显的缺陷, 它把相当大的负担放在站点服务器上和与服务器紧邻的因特网邻居上。实际上,多点传播强迫服 务器按照实时基准把各条消息发送给每个客户端,而所有这些信息必须再由服务器的邻居转发。 多点传播的大多数候选方法都试图缓解这个问题。其中一种方法是使用过去的文件共享系 统方法中的 P 2 P 模型。也就是说, 一 旦一个对等体接收到数据,它就开始把数据分发到那些正 在等待的对等体,这意味着分发的大部分问题被从数据源转移到了对等体。 另外一种候选方法称为多 路广播 ( multicast ), 它把分发问题转移给了因特网中的路由器。 使用多路广播,服务器通过单个地址把消息传送给多个客户端,依赖因特网中的路由器来识别 这个地址的含义,产生且转发消息的副本到合适的目的地。在多路广播中使用的这个地址称为 组地址,它由特别的起始位模式来标识,其他的位用来标识广播站,这在多路广播术语中称为 组。当一个客户端要从具体的站接收消息时(想要订购一个具体的组),它把这个愿望通知给最 近的路由器。这个路由器本质上把这个请求向前传送到因特网,这样其他的路由器将会把所有 带有这个组地址的信息向这个客户方向传送。简言之,当使用了多路广播,服务器只发送程序 的一个副本,而不管有多少个客户在监听,把这些信息按需复制和把它们路由到合适的目的地 则是路由器的职责。注意,依赖多路广播的应用需要因特网路由器具有超过它原来职责范围的 功能。这一功能扩展过程正在进行之中。 我们看到因特网广播和 VoIP —样,正在一边寻找立足之地,一边逐渐流行。未来到底会发 生什么并不明确,但随着因特网基础设施功能的继续扩展,网站广播的应用肯定会随之发展。 现在,嵌入式设备和家用计算机已经能够通过因特网按需传送高清视频。各种电视机、 DVD / 蓝光播放机、游戏机现在都可以直接连垮到 TCP / IP 网络,以便从众多免费或订阅服务器选择可 视内容。 问题纖 1. 第一层和第 i =: 层 isp 的怍用是什么?因特网接入服务提供商的作用是什么? 4.3 万维网 113 2. DNS 是什么? 3. 用点分 f 进制记数法表示,3.6.9_汁么位模式’層煮分十逯制记数法表示_式000纖10100011100。 4. 计箅机在因特网中的助记地址(树如 r 2 d ^: compseOTwhereu ; edu ) 结柄在哪搜方面与传统的邮翁地址 相似?在 IP 地址中也会出现同样的结构吗? 5. 列出因 特网上 发现的3种服务器,并说 ,出 ,每神的節^ . ■ ' ,:卞 ..... : .!•,];••' :;^ '; .. I :. - ':: :- - .1 ; ,: I , , ;:, :: ;.: , ; ::,•::•.::• , •:'■ :: ;: ;• ;: 1 :: :: :: ' : :- . :;' ; ' .:;• I :: .... " ■ I ' ' •::' : , 6. :为什么 A 们认为 SSH 优乎龜趋登录? 1 7. 因特网广播的 P 2 P 和多路广播方法与多点广播在广播方式上有何种不同? 8. 在4种 VdIP 中作出选择肘,应以什么为标准? 4.3 万维网 .. .把. •我. 本节将考虑一种因特网应用,多媒体信息就是通过它在因特网上传播的。它基于超文本 ( hypertext ) 的概念,超文本最初指的是包含指向其他文档的链接的文本文档,而这种链接称 为超链接 ( hyperlink )。 今天,超文本已经扩展到了包含图像、音频以及视频,而且由于其范围 的扩大,它有时候也被称为超媒体 ( hypermedia )。 使用 GUI 时,超文本文档的读者只需要用鼠标点击超链接就可以获取与它相关联的内容。 例如,假设语句“这个管弦乐队对 Maurice Ravel 的 ‘ Bolero ’ 的演奏精彩极了”出现在一个超 文本文档中,名字 Maurice Ravel 与另外一个文档链接——这个新文档也许提供了该作曲者的介 绍,则读者可以通过用鼠标点击名字 Maurice Ravel 查看相关信息。此外,如果安置了恰当的超 链接,读者还可以通过用鼠标点击名字 Bolero 收听到该音乐会的录音。 通过这种方式,超文本文档的读者就可以查阅相关的文档,或者跟随思维顺序,一个文档 一个文档地查看。各个文档的许多部分都与其他文档相链接,于是就形成了一个相关信息的相 互“缠绕”的网状组织。当在计算机网络中实现时,存在于网状组织中的这些文档就可以存放 于不同的计算机上,形成了网络范围的网状组织。在因特网上发展起来的网状组织已经遍布全 球,被称为万维网 (World Wide Web , 也称 WWW 、 W 3 或者 Web )。 万维网上的超文本文档通 常称为网页 ( Webpage )。 紧密相关的一组网页称为网站 ( Website )。 万维网要源于 Tim Berners - Lee 所作出的努力,他意识到了链接文档的想法与互联网技术结 合会产生巨大的潜力,并于1990年12月开发出了第一个实现万维网的软件。 4.3.1 万维网实现 允许用户访问因特网上超文本的软件包分为两类,即扮演客户角色的包和扮演服务器端角色 的包。客户端软件包安装在用户的计算机上,负责获取用户请求的材料,并将这些材料条理清晰 地展示给用户。客户端提供给客户一个界面,允许其在万维网上浏览。因此,客户端常被称为浏 览器 ( browser ) 或者是万维网浏览器。服务器软件包(通常称为万维网服务器 , Web server ) M 在含有待读取的超文本文档的计算机中。它的任务是根据客户端的请求提供对机器里文档的访问 权。总之,用户通过他计算机里的浏览器访问超文本文档。充当客户端的浏览器通过向遍布因特 网的万维网服务器提出请求服务来访问那些文档。超文本文档通常使用称为 HTTP (Hypertext Transfer Protocol , 超文本传输协议)的协议在浏览器与万维网服务器之间传输。 为了在万维网上定位及检索文档,每个文档都被赋予了唯一的一个地址,称为 URL(Uniform Resource Locator , 统一资源定位符)。每个 URL 都包含浏览器要连接到正确的服务器以及请求 希望的文档所需要的信息。因此,为了浏览网页,人们要首先提供给浏览器所需要文档的 URL , 114 第 4 章组网及因特网 然后要求该浏览器检索和显示这个文档。 图 4-8 给出了一个典型的 URL, 它包含以下 4 段: 与控制文档的存取的服务器进行通信的 协议,服务器所在的机器的助记地址,目录路径(供服务器找到存放该文档的目录),以及该文 档的名字。简言之,图 4-8 中的 URL 告知浏览器:使用 HTTP 协议与称为 ssenterprise.aw.com 的计算机上的万维网服务器连接,并检索名为 Julius-Caesar.html 的文档,该文档存放在 authors 目录内的子目录 Shakespeare 中。 •■■■ - ■■■ - : :_ _ _ http : //ssenterprise. av/. com/authors/Shakespeare/Julius_Caesar. html 访问该文档所需要的协 g 录路径,指示该文档在 议,这里是超文本传输 该主机文件系统中的位置 协议 ( http ) 图 4-8 典型的 URL 有时候, URL 可能不会包含图 4-8 中所示的所有段。例如,如果服务器不需要根据目录路 径去读那个文档,那么 URL 中就不会出现目录路径。此外,有时候一个 URL 只包含一个协议 以及一台计算机的助记地址。在这些情况下,该计算机的万维网服务器将返回预定的一个文档, 它通常称为主页,一般描述该网站上可用的信息。这种缩短了的 URL 使得联系各种机构的方法 变得更简单。例如, http://www.google.com 这个 URL 表示谷歌公司的主页,它包含许多超链接, 可以链接到与该公司相关的服务、产品和文档上去。 为了进一步简化网站定位,许多浏览器都 假定: 如果没有明确说明协议,就使用 HTTP 协议。 当 “URL” 只包含 www.google.com 时,这些浏览器也能准确地检索到谷歌公司的主页。 W3C ( WorM WideW 殊 Co 城 brthmi,::: 万维网硖盟)啣建于 1994 年, 宗旨是 it ; 过开发协议 标准 ( 萄# W 3 C ^ 准)乘捉进窄味网的发参。 W 3 C 总部賊在磚去^内瓦醪洲粒子氣理输所 ( c ^ m ' )命.能輊特矗秀 秦蜜: 文腾的3^杜?协说的诞 皇麥。 今天:的 W 3 亡制订了许多标 准:、 包括用于:?0!_和伴多多媒体应用 的#准 ) ,:聲些标_故得失量固轉 p 产品喪此兼容在网轉 bttp ://^ vw . w 3 eiorg 上 ,:你 可以了解 靴关于 W 3 C 的更多信息。 ^ .. 4.3.2 HTML 传统的超文本文档类似于文本文档,因为它的正文是使用诸如 ASCII 或者 Unicode 系统一 个字符接一个字符地编码的。区别是,超文本文档还包含称 为标签 (tag) 的专用符号,用于 表示该文档应该如何呈现在显示器上,该文档还需要什么多媒体资源(如图像),以及该文档的 哪些项链接到其他文档上。这个标签系统称为 HTML (Hypertext Markup Language, 超文本标记 语言)。 因此,按照 HTML 的要求,网页的作者描述了浏览器所需要的信息,使得浏览器能够将该 页呈现在用户的显示器上,并找到当前网页所引用的任何相关文档。该过程类似于向简单输入 4.3 万維网 115 的文档加入排版命令(比如用红笔标注),以便排版人员知道文档最后应该以什么形式出现。对 于超文本, HTML 标签就是这些红色记号,浏览器最终充当了排版人员的角色,读取 HTML 标 签就会知道文本应以什么方式呈现在计算机显示器上。 图 4-9 a 给出了一个非常简单的网页的 HTML 编码版本(称为源版本)。注意,标签是用 符号“”括起来的。 HTML 源文档由两段组成——首部(以标记 引导, W head > 结束)和主体(由 引导,以 body > g 束)。网页首部和主体之间的区别类似于办公 室备忘录的首部与主体之间的区别,两者 都是: 首部包含文档的预备性信息(例如备忘录的 日期、主题等);主体包含文档的实质内容,对于网页就是该页可能会呈现在计算机显示器上 的内容。 指示文档开 始的 标签: 预备信息^ 示的网页部分 指示文档结 束的标签— My Web Page Click here for another page . v_ / ( b ) 显示在计算机屏幕上的网页 图 4-9 —个简单的网页 图 4-9 a 给出的网页的首部只包含了该文档的标题(用出化标签^^住)。该标题只是用于文档 编制目的,并不会显示在计算机显示器上。要显示在显示器上的内容包含在文档的主体内。 图 4-9 a 所示的文档主体的第一个条目是一个包含文本 “My WebPage ” 的一级标题(在 和匕1>标签之间)。一级标题意味着浏览器要将该文本明显地呈现在显示器上。主体的下一个 条目是文本的一个段落(在和之间),包含文本 “Click here for another page . ”。 图 4-9 b 给出的是浏览器显示在计算机显示:^上的页面。 根据它现在的形式,图 4-9 所示的页面是没有实际意义的。用户单击 here 时,什么也不会发 生,虽然网页上暗示了如此操作会使得浏览器打开另外一个网页。为了引发相关的动作,我们 必须将单词 here 与另外一个文档建立链接。 假设单击 here 时,我们打算让浏览器呈现 URL 为 http :// crafty . com / demo.html 的网页。首先, 116 第 4 章组网及因特网 我们要用标签 0 和 &> 在该网页源版本中将单词 here 括住,这对标签称为锚标签。在开锚标 签里,我们要插入 参数: href = http :/ /crafty.com/demo.html (如图 4-10a 所示)它表示与该标签相联系的超文本引用 ( href ) 是在等号后面的 URL (http://crafty.coxn/demo.litml )。 加上错标签后,该网页就会如图 4-10b 所不呈现在计算机显不器上 To 注意,它与图 4-9 b 是一样的,只是单词 here 用彩色突出显示,表示它是到另外一个网页文档 的链接。单击这类突出显示的术语就会使得浏览器检索并显示相关的网页文档。因此,网页文 档通过锚标签在彼此之间建立起了链接。 含有参数^ 的锚标签1 闭锚标签一 [ (a) 用 HTML 编写的网页 My Web Page Click here for another page . ( b ) 显示在计算机屏幕上的网页 图 4-10 增强的简单网页 最后应该简要说明一下,图像是如何加入我们这个简单的网页的。为此,假设要插入的图 像的 JPEG 编码是存储在 Images.com 的 Images 目录中的文件 OurPic.jpg , 且对那一位置的万维网服务 器可用。这样,在1111\11^文档的标签后插入图像标签»我们就可以命令浏览器在该网页的顶部显示该图像了。这告诉浏览 器的是名为 OurPic.jpg 的图像应该在该文档的开头显示。 ( src 是 source 的缩写,意思是等号后面 的信息表示的是要显示的图像的来源)。浏览器发现这个标签时,就会传输给位于 Images.com 的 HTTP 服务器一条报文,请求 OurPic.jpg 图像,并且恰当地显示该图像。 如果我们将该图像标签移到文档的后面,就在标签前面,那么该浏览器就会在该 _4.3 万維网 117 网页的底部显示该图像。当然,在网页上定位图像还有许多复杂的技巧,但是现在我们不必关 心这些问题。 4.3.3 XML HTML 本质上是一个符号系统,文本文档及其外观都可以通过它编码成一个简单的文本文 件。同理,我们也可以将非文本内容编码成文本文件,例如乐谱。乍一看,传统上表示音乐的 五线谱,节拍以及音符都不符合文本文件规定的一个字符接着一个字符的格式。不过,我们可 以通过另外一种符号系统来克服这个困难。精确地说,我们规定用 表示五线谱的开始,用 313 土 f >表示五线谱的结束,用 2/4 time > 表示节拍号, 用 〈 measure 〉 和 rueasure > 分别表示小节的开始和结束,用 egth C notes > 表 示八分音符 C , 等等。 那么, 文本: C minor 2/4 egth egth G, egth G, egth G hlf E /measure> 就可以用于编码图 4-11 所示的五线谱 D 使用这种符号,乐谱就可以作为文本文件编码、修改、 存储和在因特网上传输。此外,还可以编写软件,将这类文件的内容以传统乐谱的形式表现出 -来,甚至可以用一个音乐合成器来演奏此音乐。 注意,我们的乐谱编码系统沿用了 HTML 使用的文体。对于标识组成部分的标签,我们选 择用符号“”作为定界符。我们选择用相同的标签名表示结构(如五线谱、一串音符, 或者是一个节拍)的开始和结束,表示结束的那个标签有一条斜线(以 幵始的就以 measure > g 束)。我们选择用诸如 clef = " treble ” 的表达式来指示标签中特定的属性。这 种文体还可以用于开发表示其他格式的系统,如数学表达式和图表。 XML (extensible Markup Language , 可扩展标记语言)是一种标准化的文体(类似于上面 乐谱的例子),用于设计将数据表示为文本文件的符号系统。事实上, XML 是从比较老的称为 SGML ( Standard Generalized Markup Language ,标准通用标记语言)的标准派生出的简化版。 遵照 XML 标准,人们己经开发出了一批称为标记语言 (markup language ) 的符号系统,可以表 示数学、多媒体演示以及音乐。事实上, HTML 就是一种基于 XML 标准开发的表示网页的标记 语言。(实际上, HTML 的原始版本在 XML 标准巩固之前就已经开发出来了,因此 HTML 的一些 特征不严格遵守 XML 。 正是这个原因,我们可能需要参考 XHTML , 它是严格遵守 XML 的 HTML 版本。) 关于如何设计标准以获得广泛应用, XML 是个很好的范例。为了编码各种类型的文档,我 们不应设计出单独的、无关联的各种标记语言, XML 所用的方法是开发一种通用标记语言标准, 通过这个标准,就可以为各种应用幵发不同的标记语言了。这样开发出来的标记语言具有一致 性,可以组合起来获得复杂应用的标记语言,例如包含乐谱片段和数学表达式的文本文档。 118 第 4 章组网及因特网 最后要说明的是, XML 允许开发与 HTML 不同的新的标记语言,因为新的标记语言强调的 是语义而不是词本身。例如,使用 HTML 可以标记菜谱里的配料,使得它们以列表形式出现, 每一种配料单独占一行。不过,如果我们使用面向语义的标签,菜谱里的配料就可以标记为配 料(也许使用标签 〈 ingredient >和 ingredient >) 而不仅仅是列表中的各个项。这个区别 很细微但是很重要。语义方法使得搜 索引擎 (search engine , 帮助用户搜索与特定主题相关的网 页信息的网站)能够确定哪些菜谱包含或者不包含某些配料,这将是现在搜索引擎技术的一项 重大改进,因为现在的技术只能够分离出含或者不含有某些单词的菜谱。精确地说,如果使 用语义标签,搜索引擎就可以找到不包含菠菜的卤汁宽面的菜谱,而只根据单词内容进行的类 似搜索就会跳过以“这个卤汁宽面不包含菠菜”开始的菜谱。同样,如果根据语义而不是词本 身使用因特网范围的标准标记文档,那么创建的是万维“语义”网,而不是现在使用的万维“语 法”网。 4.3.4 客户端和服务器端的活动 现在我们来考虑一下,检索图 4-10 所示的简单网页,并将其呈现在浏览器所在的计算机的 显示器上,都需要哪些步骤。首先,扮演客户端角色的浏览器要使用 URL (也许是从使用该计 算机的人那里获得)里的信息,与控制对该网页访问的万维网服务器建立连接,然后请求它传 输该页的一个副本。服务器然后要将图 4-10 a 上显示的文本文档发送给浏览器作为回应。接着, 浏览器将解释该文档中的 HTML 标签,以确定如何显示该网页,并将文档呈现在计算机的显示 器上。浏览器的用户就将看到如图 4-10 b 所示的图像。如果用户接着用鼠标单击单词 here , 浏览 器将使用相关联的锚标签中的 URL 链接到恰当的服务器,以获得并显示另一个网页。总之,整 个过程就是浏览器按照用户的要求搜索及显示网页。 但是,如果我们想获得一个带有动画的网页,或者是一个允许客户填写订单并提交的网页 呢?这些需求就需要浏览器或万维网服务器付出额外的行动。如果这些行动由客户机(如浏览 器)完成则称为 客户端 ( client - side ) 活动,如果由服务器(如万维网服务器) 完 成则称为服务 器端 ( server - side ) 活动。 例如,假设旅行社想要客户能确认想去的目的地和旅行日期,它将呈现给客户一个定制的 网页,只包含与该客户需求有关的信息。在这种情况下,旅行社的网站将首先呈现给客户一个 包含可供选择的旅游目的地的网页。客户根据这个信息,指定感兴趣的目的地和旅行日期(客 户端活动)。这些信息将传回给旅行社的服务器,服务器利用这些信息来构建合适的定制网页 (服务器端活动),再将这个网页发送给客户的浏览器。 举一个搜索引擎服务的例子。在这种情况下,客户端的用户指定感兴趣的主题(客户端 活动),然后这个主题被传送给搜索引擎,在那里会构建一个包含用户可能感兴趣文档的定制 网页(服务器端活动),然后发回给客户端。还有一个例子就是网 站邮件 (Web mail ) ——一种 日益流行的方法,通过它计算机用户能通过网页浏览器来访问他们的电子邮件。在这种情况下, 万维网服务器是客户与客户邮件服务器间的中间层。从本质上讲,万维网服务器构建包含有来 自邮件服务器信息的网页(服务器端活动),把这些网页传送给客户端,由客户端浏览器显示它 们(客户端活动)。相反,浏览器支持用户创建消息(客户端活动),把这一信息传送给万维网 服务器,万维网服务器再把这些消息转发给邮件服务器(服务器端活动)。 实现客户端和服务器端活动的系统有很多,可谓争奇斗艳,各有千秋。一种早期但现在仍 然很流行的控制客户端活动的方法是,在网页的 HTML 源文档中包括用 JavaScript 语言(由网景 公司开发)编写的程序。浏览器可以从中获取程序并根据需要执行。另外一种由 Sun 公司开发的 方法是,首先将一个网页传输给浏览器,然后将称为小应用程序 ( applet , 用 Java 语言编写)的 *4.4 因特网协议 119 额外程序单元根据 HTML 源文档的要求传输给该浏览器。还有一种方法是使用由 Macromedia 公 司®开发的 Flash , 可以实现大量多媒体客户端演示。 控制服务端活动的一个早期方法是,使用一组称为 CGI (Common Gateway Interface , 公共 网关接口)的标准,客户通过它可以请求执行在服务器中存储的程序。这种方法的一个变体(由 Sun 公司开发)是允许客户在服务器端执行称为小服务程序 ( servlet ) 的程序单元。如果所请求 的服务器端工作是创建定制的网页,如旅游代理的例子,那么就可以使用小服务程序方法的一 种简化版本。这时,称为 JSP (Java Server Page , Java 服务器页面)的网页模板存放在万维网服 务器里,并利用从客户端接收到的信息完成该网页。微软公司采用了类似的方法,构建定制网 页的模板称为 ASP (Active Server Page , 活动服务器页面)。与这些专有系统相对, PHP 是一种 实现服务器端功能的开源系统。 PHP 最初代表个人主页 (Personal Home Page ), 现在指的是 PHP 超文本处理程序 (PHP Hypertext Processor ) 。 最后,我们应清醒地认识到,由于允许客户机和服务器在对方的计算机上执行程序,会引 发一些安全性问题和道德问题。万维网服务器要固定地给客户传输要执行的程序,这个事实给 服务器端带来了道德问题,并相应地给客户端带来了安全性问题。如果客户机盲目地执行万维 网服务器发送来的任何程序,它就为服务器的恶意行动打幵了大门。同理,客户机可以让程序 在服务器端执行,因此也给客户端带来了道德问题,相应地给服务器端带来了安全性问题。如 果服务器端盲目地执行客户机发送过来的任何程序,那么安全性就被破坏了,并因此产生潜在 的危险 问题与綠习 •一 .. _ _ --- . . , 1. 什么捧 JRL ? 什么是濟 2. 什么是标记语言? : : v .3, HTHE 郝5^昀 区别每 _衾々 ; 3 '| :議麵議麵■,震 讓議 J 賴纏 I : 5. “户廳服务躺分顯 g 什么 ? *4.4 因特网协议 本节研究报文是如何在因特网上传输的。因为传输过程需要系统中所有计算机合作,所以 控制传输过程的软件需要驻留在因特网的每台计算机中。我们首先研究此类软件的总体结构。 4 A 1 因特网软件的分层方法 网络软件的首要任务是提供从一台机器到另一台机器传输报文所需的基础设施。在因特网 上,报文传递活动是通过软件单元的层次结构完成的,这和你把一份礼物从美国的西海岸邮寄 到东海岸的过程类似(见图4-12)。第一步,把礼物打包并在包裹外面写上正确的邮寄 地址; 接 着,把包裹拿到运输公司,例如 邮局; 运输公司把这个包裹和其他包裹一同放入一个大的集装 箱,并送往与其签有服务合同的航空 公司; 航空公司将集装箱装入飞机并运往目的城市,也许 沿途还要经过中 转站; 到达目的地,航空公司把集装箱从飞机上卸下,然后送往当地的运输公 司; 接着,运输公司把你的包裹从集装箱取出并送给收件人。 ①己被 Adobe 公司收购。 120 第 4 章组网及因特网 准备运送 的包裹 Siii 把包裹放入集装 箱以便交给航空_ 将集装箱放 到飞机上 中转站 将集装箱送到另一架飞机 图 4- 12包裹运输的例子 目的地 取出并打 开包裹 从集装箱取出包 裹送给收件人 将集装箱送 到运输公司 简而言之,礼物的运输过程需要3层组织 : (1) 用户层次(包括 你和你的朋友 ):(2) 运输 公司; (3) 航空公司。每一层把下一层当做 抽象工具来使用。(你不关心运输公司的工作细节,而运输公司也不需 要关心航空公司的内部运作。)组织的每一层在源端和目的端都有代 理,在目的端的代理完成在源端相应代理的相反工作。 因特网上软件控制通信的过程和运输包裹的情况类似,但因特网 上的软件有4层而不是3层,每层所涉及的是软件例程而不是人和企业。 这4层就是众所周知的应用层 (application layer )、 传输层 (transport layer )、 网络层 (network layer ) 、 链路层 (link layer ) (图 4-13)。 通常, 由应用层产生一个报文,当这个报文准备发送时,从应用层向下传递, 经由传输层和网络层,最后传递到链路层进行传输。目的地的链路层 接收这条报文,沿逆向分层结构向上传递,直到把它交给目的地的应图 4-13 特网软件层-次 用层。 下面通过跟踪一个报文通过网络系统的路径,从总体上研究一下报文的传输过程(参见图 4-14)。首先从应用层开始。 应用层由那些使用因特网通信来完成任务的软件单元组成,如客户机和服务器软件。虽然 名称类似,但是这一层不局限于 3.2 节介绍的软件分类中的应用软件,还包括一些实用软 件包。 例如,使用 FTP 协议传输文件的软件和利用 SSH 提供远程登录功能的软件已经很普遍,所以通 常称它们为实用软件。 在因特网上,应用层使用传输层发送和接收报文的方式与我们通过运输公司邮寄和接收包 裹非常相似。正像我们有责任提供一个和运输公司所要求的规范一致的地址一样,应用层负责 向因特网基础设施提供兼容的地址。为了满足这个要求,应用层利用因特网上的域名服务器提 供的服务把便于人类记忆的地址翻译成符合因特网规范的 IP 地址。 传输层的重要任务是从应用层接收报文并确保报文以正确的格式在因特网上传输。为了第 二个目的,传输层将长报文分成小的片段作为独立单位在因特网上传输。,因为在因特网内一个 长报文会阻塞许多报文必经的节点,所以对长报文的分段是必需的。实际上,小段的报文在这 些节点可以交叉通过,而一个长报文在经过这些节点时将迫使其他报文等待(很像在铁路交叉 道口,许多小汽车等一列长火车通过的情况)。 *4.4 因特网协议 121 W 龜纖爲座獻:感 feg # e | 賴 ?;s* »■ 較 tr^ \>ri ^雄: mmA ■ T^ym %W _^;.® 磬 辑幾: 變 .3 壽繼 # t ■JVto'tilt. , 丄 o,. .4 科 w 知 d - : :. 羔找〜:切斧 ;^! _ 复习题 (带 * 的题目涉及选读小节的内容。) 1. 什么是协议?说出本章介绍的 3 个协议,并描 述每个协议的目标。 2. 描述客户机/服务器模型。 3. 描述对等模型。 4. 说出 3 种分布式计算机系统。 5. 开放式网络和封闭式网络之间的区别是什么? 6. 为什么 CSMA/CD 协议不能应用于无线网络? 7. 描述在使用 CSMA/CD 协议的网络内,一台机 器发送报文所要遵循的步骤。 8 . 什么是隐藏终端问题?描述解决它的技术。 9. 集线器和中继器怎样区分? 10. 路由器和中继器、网桥及交换机这类设备怎样 区分? 11. 网络和因特网的区别是什么? 12. 说出网络中用来控制报文发送权的两个协议。 13. 使用 32 位因特网地址原先被认为是提供了足 够大的扩展空间,但这推测被证实并不准确。 IPv6 使用 128 位地址,这将被证明是足够的 吗?证明你的答案。(例如,你可以把可能的 地址数目与世界的人口进行比较。) 14. 用点分十进制记数法为下列位模式编码。 a. 000001010001001000100011 b. 1000000000100000 c. 0011000000011000 15. 下列点分十进制记数法表示的位模式分别是 什么? a. 0.0 b ‘ 26.19.1 c. 8.12.20.13 16. 假设因特网上一台终端系统的地址是 134.48.4.122, 那么这个 32 位地址如何用十六进 制记数法表示? 17. 什么是 DNS 查找? 18 . 如果一台计算机的助记因特网地址是 batman.batcave.metropolis.gov, 推测一下该机 器所在域的结构是什么样的? 19. 解释电子邮件地址 [email protected] 的 组成。 20. 在 VoIP 语境下,模拟电话适配器和嵌入式电 话的区别是什么? 21. 邮件服务器的功能是什么? 22. 多点传播与多路广播的区别是什么? 23. 给出下列名词的定义。 a. 域名服务器 b. 因特网接入服务提供商 c. 网关 d . 终端系统 24. 给下列名词的定义。 a. 超文本 b. HTML c. 浏览器 25. 因特网的许多‘‘外行用户”经常混用因特网和 万维网这两个术语^这两个术语的正确含义是 指什么? 26. 在浏览一个简单的网页文档时,让浏览器显示 该文档的源版本,然后说出该文档的基本结 构。特别是,说出该文档的首部和主体,并列 出你在首部和主体中发现的一些语句。 27. 列出 5 种 HTML 标签,并说出它们的含义。 28_ 修改下面的 HTML 文档,使单词 Rover 链接 到 URL 为 http://animals.org/pets/dogs.html 的 文档。 Example My Pet Dog

My dog's name is Rover.

29. 画一个草图来说明下面的 HTML 文档在计算 机屏幕上的显示信息。 社会问题 131 Example My Pet Dog 30. 使用本章介绍的非正规 XML 风格来设计一 个标记语言,把简单的代数表达式表示为文 本文件。 31. 使用本章介绍的非正规 XML 风格设计一组标 签,文字处理器可能用到这些标签标记潜在的 文本。例如,一个文字处理器如何指示出什么 文本应该是粗体、斜体、有下划线等? 32. 用本章介绍的非正规 XML 风格来设计一组标 签,使得可以根据文本项在打印页上的出现方 式给电影评论做标记。然后再设计一组标签, 可以用于根据文本中这些项的含义标记电影 评论。 33. 用本章介绍的非正规 XML 风格来设计一组标 签,可以用于根据文本项在打印页上的出现方 式给运动比赛项目的文章做标记。然后再设计 一组标签,可以用于根据文本中这些项的含义 标记这些文章。 34. 说出下面 URL 的组成,并描述各项的含义。 http://lifeforms.com/animals/ moviestaxs/kermit.html 35. 说出下列缩写 URL 的组成。 a. http://www.fanntools.org/ windmills.html b. http://castles.org/ c. www.coolstuff.com 36. 如果要浏览器在下列两个 URL “找文件”,浏 览器的动作有什么不同? http://stargazer.universe.org https:// stargazer.uni verse. org 37. 给出万维网上两个客户端活动的例子和两个 . 服务器端活动的例子。 *38. 什么是 OSI 参考模型? *39. 在一个基于总线型拓扑结构的网络里,总线对 于要传送报文的机器是必须竞争的不可共享 资源。在这种环境里死锁(参见选读的 3.4 节) 是如何控制的? *40. 列出因特网软件层次结构的 4 层,并说明各层 所完成的任务。 *41. 为什么传输层把长报文划分为小分组? * 42 . 当某应用程序要求传输层使用 TCP 来传输报 文时,为了满足应用层的要求,传输层需要附 加什么样的报文? *43. 在实现传输层时,什么情况下 TCP 优于 UDP? 什么情况下 UDP 优于 TCP ? *44. 说 UDP 是无连接协议的含义是什么? *45. 在 TCP/IP 协议层次结构里,为 了用下 列方法 过滤进来的通信流,防火墙应该设置在哪一 层? a. 报文内容 b. 源地址 c. 应用类型 46. 假定你想建立一个可以过滤掉包含某些术语 和短语的电子邮件报文的防火墙。这个防火墙 应该放在域的网关上,还是放在域的邮件服务 器上?说明理由。 47. 什么艮务器峨麵麵艮务处? 48. 总结公钥加密的原理。 49. 一台空闲但未设防的 PC 是如何威胁因特网的? 50. 对于整个因特网界限制用法律来解决因特网 存在的问题,你如何看待? 社会问题 下面的问题有助于分析一些与计算领域相关的伦理、社会和法律问题。回答这些问题不是 唯一的目的,还应该考虑为什么这样回答,以及你的判断是否对每个问题都标准如一。 1. 计算机网络使得在家办公的观念流行起来。这种变化有哪些利弊?它对自然资源的消费 有什么影响?它会使家庭巩固吗?它会减少“办公室政治”吗?在家里办公的人和在现 场办公的人会有同样的职务晋升机会吗?社会联系会被削弱吗?减少和同行之间的个人 接触会有正面的还是负面的影响? 132 第 4 章组网及因特网 2. 网上购物正在变成“亲身”购物的一个替代行为。这种购物习惯的变化对于社会有什么 影响?对于大型购物中心的影响呢?对于你通常只逛不买的,比如书店和服装店之类的 小店呢?以尽可能最低价格购买,好到什么程度,坏到什么程度?你是否有这样的道义 上的责任,多花一点钱购买一个商品来支持本地的 商业? 比较本地商店里的商品,然后 通过因特网以较低的价格订购,这合乎道德吗?这种行为的长期影响会是什么? 3. 政府对其公民访问因特网(或其他国际性网络)的控制应当限制在什么程度内?对于涉 及国家安全的问题呢?可能发生哪些安全问题? 4. 电子公告牌允许网络用户发布消息(常以匿名方式)和阅读其他用户发布的消息。这个 公告牌的管理人员应该对内容负责吗?电话公司应该对电话的通话内容负责吗?食品杂 货店的管理者要对店内的社团公告牌内容负责吗? 5. 因特网的使用应当被监视吗?应当被管制吗?如果需要,应该由谁来管理,管理到什么 程度? 6. 你花费多少时间来使用因特网?那些时间花得值吗?上网改变你的社会活动了吗?你认 为通过因特网与人交谈比面对面与人交谈更容易吗? 7. 当你为个人计算机买了一个软件包的时候,开发商通常要你向开发商注册,以便你可以 得到未来升级的通知。这种注册过程越来越多地通过因特网处理,经常要你提供诸如姓 名、地址以及如何知道产品等信息,然后开发商的软件自动把这些数据传输给开发人员。 如果开发商设计的注册软件在注册过程还把额外的信息发送给开发人员,那么会发生什 么道德问题吗?比如,注册软件可能扫描你的系统内容,报告找到的其他软件包。 8. 访问一个网站时,这个站点有在计算机内记录数据(称为 cookie ) 的能力,从而表明你曾 经访问过该站点。然后这些 cookie 可被用来识别回访的访问者并记录他们以前的活动,以 便网站可以更高效地应对访问者对网站的未来访问活动。计算机上的 cookie 还可提供你访 问网站的记录。网站应该有在计算机内记录 cookie 的功能吗?未经你的同意,是否应允许 网站在你的计算机里记录 cookie ? cookie 可能的好处是什么?使用 cookie 可能会引发什么 问题? ^ 9. 如果政府机构要求公司注册加密密钥,公司还是安全的吗? 10. 一般来说,出于礼貌我们不会为了安排周末外出之类的个人或社团的事情而给在工作场 所的朋友打电话。类似地,大多数人也不愿意打电话到客户的家里介绍新产品。按照类 似的习俗,我们把婚礼请柬寄到客人的住所,而把商务会议的通知邮寄到出席者的工作 地址。把给朋友的私人电子邮件通过他工作的地方的邮件服务器发送合适吗? 11. 假定一个 PC 的所有者让这台 PC 接入因特网,但最终这台电脑被其他人用来进行拒绝服务攻 击。这个 PC 的所有者该负多大的责任?你的回答和他是否安装了正确的防火墙有关吗? 12. —个生产糖果和玩具的公司在他们的公司网站上提供游戏,在推荐公司产品的同时让孩 子们娱乐,这种做法道德吗?如果游戏是用来收集小孩信息的,那又如何?娱乐、广告 和利用之间的界限是什么? 课外阅读 _ Antoniou, G. and F. van Harmelem. A Semantic Web Primer . Cambridge, MA: MIT Press, 2004. Bishop, M. Introduction to Computer Security . Boston, MA: Addison-Wesley, 2005. Comer, D. E. Computer Networks and Internets , 5th ed. Upper Saddle River, NJ: Prentice-Hall, 2009. Comer, D. E. Internetworking with TCP / IP , vol_ 1 , 5th ed. Upper Saddle River, NJ: Prentice-Hall, 2006. Goldfarb, C. F . and P. Prescod. The XML Handbook , 5th ed. Upper Saddle River, NJ: Prentice-Hall, 2004. 课外阅读 133 Halsal , F_ Computer Networking and the Internet. Boston, MA: Addison-Wesley, 2005. Harrington, J. L. Network Security: A Practical Approach. San Francisco: Morgan Kaufinann, 2005. Kurose, J. F. and K. W. Ross. Computer Networking: A Top Down Approach Featuring the Internet, 4th ed. Boston, MA: Addison-Wesley , 2008. Peterson, L. L. and B. S. Davie. Computer Networks: A Systems Approach, 3rd ed. San Francisco: Morgan Kaufinann, 2003. Rosenoer, J. CybefLaw, The Law of the Internet. New York: Springer, 1997. Spinello, R. A. and H. T. Tavani. Readings in CyberEthics. Sudbury, MA: Jones and Bartlett , 2001. Stallings, W. Cryptography and Network Security, 4t±i ed. Upper Saddle River, NJ: Prentice-Hall, 2006. Stevens, W. R. TCP/IP Illustrated , vol. 1. Boston, MA: Addison-Wesley , 1994. 绪论”一章中我们得知,计算机科学的核心主题是对算法的研究。现在是我们关注这个 核心主题的时候了。我们的目标是探究足够的基本素材来真正地理解和认识计算科学。 本章內容 :感:.::痛螓鉍就.:::編 ;;:,;:;:!:, : v ,;:!;: 托.幻 j :; 潑您轉 虐議義 ^^8麵_顯誦讎 ^5:遂1|3结构 :: f ::顯 ■:於 ㈤ .:强::閨 •• * :. ' . .1 - - I— -.V •• . J..' ' :.J I ■: 我们已经知道,在计算机能够执行一个任务之前,必须给出一个算法精确地告诉计算机做 什么。因此,算法的研究是计算机科学的基石。在本章中,我们将介绍算法研究的许多基本概 念,包括算法的发现和表示以及算法的主要控制结构——迭代和递归等问题。在讲解这些问题 的同时,我们还会介绍几个有关查找和排序的著名算法。下面首先介绍算法的概念。 5.1 算法的概念 _ 在“绪论” 一 章中,我们把算法非正式地定义为描述如何完成任务的步骤集。在本节中, 我们将进一步讨论算法的基本概念。 5.1.1 概览 在前面的学习中,我们已经遇到了多个算法。我们发现了用来进行数制转换的算法、检测和纠 正数据错误的算法、压缩和解压缩数据文件的算法、在多任务环境中控制多道程序设计的算法以及 很多其他算法。此外,我们已经看到, CPU 所遵循的机器周期只不过是下面这个简单算法。 只要未发出停机指令就执行以下步骤: a. 取一条 指令; b . 解码该 指令; c. 执行该指令。 就像图 0-1 中的魔术的算法所展示的那样,算法并不局限于技术活动,实际上它甚至可以 用来描述剥豌豆壳这样的普通活动。 获得一篮子未剥壳的豌豆和一只空碗。只要篮中还有未剥壳的豌豆就执行下面的 步骤: a. 从篮子里拿出一个 豌豆; b . 剥开豌豆的 豆荚; 5.1 算法的概念 135 C. 把剥落的豆放到碗 里面; d . 扔掉空豆荚。 实际上,许多研究人员相信,人脑中的每一个活动,包括幻想、创造和决策,实际上都是 算法执行的结果——我们将在学习人工智能(第11章)时介绍。 但是,在继续深入研究之前,让我们先考虑一下算法的正式定义。 5.1.2 算法的正式定义 非正式、不严格地定义的概念在日常生活中是可接受的并且是很常见的,但是科学必须基 于严谨定义的术语之上。现在,我们就来考察一下图 5-1 中算法的正式定义。 算法是定义一个可终止过程的一组有序的、无歧义的、可执行的步骤的集合。 图 5-1 算法的定义 注意,该定义要求一个算法中的步骤集合是有序的。这意味着,一个算法中的各个步骤必 须有一个就执行顺序而言非常明确的结构。这并不意味着这些步骤必须从第1步到第2步,再 到下一步,这样顺序执行。有些算法,称为并行算法,包含的步骤序列不只一个,每一个序列 都被设计成由多处理器机器中的不同处理器执行。在这种情况下,整个算法并不只包含一个遵 照第1步、第2步这样的顺序的执行流,其结构是一种多执行流结构,这些执行流在整个任务 的不同部分被不同的处理器执行时不断分支和再接合(我们会在第6章中再次讨论这个概念)。 其他例子包括第1章中所讲述的触发器电路执行的算法,这个电路中每一个门电路都完成整个 算法的一步。这里,这些步骤是按照因果关系排列的,每个门电路的结果都是通过电路传播的。 接下来,我们考虑算法必须由可执行的步骤组成这一要求。为了满足这个条件,我们考察 下面这条 指令: 给出一个所有正整数的列表。 | 由于正整数有无穷多个,完成这条指令是不可能的。因此,任何包括这条指令的指令集都不能 称作一个算法。计算机科学家使用有效的 ( effective ) 这个术语来表示可执行的概念。也就是说, 说算法中的一个步骤是有效的就意味着它是可执行的。 图 5-1 中的算法定义的另外一个要求是算法中的步骤必须是无歧义的。这意味着在算法的 执行过程中,正在被处理的信息必须足以唯一地、完整地确定每一步所需要的动作。换句话说, 算法中的每一步的执行都不需要创造性的技能,只要求遵照指令执行。(在第12章我们将学习 的算法称为非确定性算法,但那个算法不受这里的限制,属于另外一个重要的研究论题。) 图54给出的定义还要求,算法定义的是一个可终止的过程,也就是说,一个算法的执行 必须能够最终结束。这个要求源自理论计算机科学,其目标是要回答诸如“算法和机器的最终 限制是什么?”之类的问题。其中,计算机科学试图寻找下面两种问题的 区别: 哪些问题的答 案的获得在算法系统能力范围之内,哪些问题的答案的获得超出了算法系统能力范围。从这个 意义上讲,它在以一个答案告终的过程与一个只能向前执行而不能得到结果的过程之间存在着 一条分割线。 可是,还是有一些不可终止的过程是非常有意义的,包括监视病人的生命特征和维持飞行 器的飞行高度等。有些人可能辩称这些问题仅仅是算法的重复,这其中的每一个算法都会在到达 结束状态之后自动重复执行。另外一些反对这一论点的人可能认为,这种说法只不过是一种对 于正式定义限制的过度坚持。不管是哪种情况,结果都是算法这个名词通常使用在对步骤集合 136 第 5 章算 法 的实用的或者非形式的引用,并不一定必须定义一个可终止的过程。例如,当1除以3的时候, 长除法“算法”并不定义一个可终止的过程。从技术上讲,这些例子都表示对该术语的误用。 5.1.3 算法的抽象本质 强调算法与其表示的区别是非常重要的,这就好像一个故事和一本书的差别。一个故事本 质上是抽象的,或者说是概念上的,而一本书是一个故事的物理表示。如果一本书被翻译成其 他语言或者以另外一种样式出版,仅仅是这个故事的表示形式改变了,而故事本身并没有变化。 同样,算法是抽象的,与它的表示是有差别的。一个算法可以用多种方式来表示。比如, 在华氏温度和摄氏温度之间进行转换的算法可以用下面的代数公式 表示: 但也可以用下面的指令表示: 将摄氏温度数值乘以|,然后在乘积上加32。 甚至可以用电路的形式予以表示。无论哪种情况,基本的算法是一致的,只不过是表示方式不 同罢了。 算法和它的表示的区别体现了我们在传达一个算法的时候存在的问题。 一 个常见的例子 是,一个算法必须描述到什么样的细致程度。对于气象学家来说,指令“将摄氏度读数转换 为相应的华氏度数”就足够了,但是,对于一个外行来说(需要更加详细的描述)这个指令 可能是模糊的、有歧义的。然而,问题并不在于算法,而是算法并没有很好地按照外行所要 求的细致程度进行表示。在 5.2 节中,我们会学习原语的概念,并看到原语是如何被用于消除 算法表示中的这种歧义性问题的。 最后,在算法和它的表示这个问题上,我们应该明确另外两个概念的区别——程序和进程。 程序是一个算法的表示。(这里,我们没有在正式意义上使用术语算法,因为许多程序是不可终 止的“算法”的表示。)实际上,计算机科学家通常用程序这个词表示那些设计成计算机应用程 序的算法的表示。在第3章中,我们把进程定义为执行程序的活动。然而,请注意执行一个程 序就是执行由该程序所表示的算法,所以一个进程可以等价地定义为执行一个算法的活动。我 们可以得到这种 结论: 程序、算法和进程既是不同的却又有关联的。程序是算法的表示,而进 程又是执行算法的活动。 !!f!!;:!i|;! i : ii 丨: ::諧 : m : 钃酬 ___ss_ If 露 ili ;:: 賴纖驗 to 的区 % 给出十:酱‘所熱審擁字 v : 它们是^«虐.义土的_法吗? ' : :-; 3/对于_1节中给__ 法的# E 式定义,^在咖些有也义的(食# 的〉 地芳^ 4. 从什么麵!上说,摄不列搐令__描__骤不能_算法?- 第3*歩:#翁的口袋 SI 取出一_械并 i 把舍 k 到桌芋上。 二 第2步、第 1 步 、:I / ; „ . .. 卜 ii.._ 關 5.2 算法的表示 在本节中,我们考虑与算法表示有关的问题。我们的目标是引入原语和伪代码的基本概念, 5.2 算法的表示 137 并且建立一种为我们所用的算法表示系统。 5.2.1 原语 一个算法的表示需要使用某种形式的语言。对于人类,这可能是一种传统的自然语言(英 语、西班牙语、俄语、日语),也可能是一种图形语言,如图 5-2 所示,在这个图中,我们给出 了用一块正方形纸片叠出一只鸟的算法。然而,这种自然的沟通方式常常会引起误解,有些时 候是因为算法描述中使用的术语可能拥有多种含义。例如,句子 “Visiting grandchildren can be nerve-racking." 可能表示孙子来访时会惹出事情,也可能表示去看孙子是一件很费周折的事情。 所需的详细程度引发的误解也可能导致问题。很少有读者能够按照图 5-2 给出的步骤成功地叠 出一只小鸟来,但是一个专门学习折纸的学生可能很轻松地就将其完成。简言之,当用来描述 算法的语言并没有被准确定义或者并没有给予足够详细的信息的时候,交流就会产生问题。 计算机科学解决这些问题的途径是建立一组严格定义的构建块,利用它们来构建算法的表 示。这种构建块称 作原语 (primitive)。 赋予原语准确的定义消除了很多由于歧义造成的问题, 并且要求按照这些原语来描述算法就是确定了一致的细致程度。原语的集合以及说明如何组合 这些原语来表示比较复杂的想法的规则集合就构成了一种程 序设计语言。 每个原语都有自己的语法和语义。语法是原语的符号表示,语义是指该原语的含义。 air — 词的语法由3个符号组成,然而其语义是一种充满整个世界的气体物质。作为一个例子,图 5-3 描述了折纸术中使用的一些原语。 语法 将纸翻面,如 语义 纸的一面用 阴影表示 1 K 分纸的两面,如 o 表示凹折叠线 使得 表示 表示凸折叠线 使得德 表示 向上折 使得 折成 推进去 使得 图 5-3 折纸术的原语 折成 为了获得用来描述由计算机执行的算法的原语的集合,我们需要借助于计算机执行的单个 指令。如果一个算法在这个级别上加以描述,我们肯定会得到一个适合计算机执行的程序。然 而,在这个级别上描述的算法是非常单调的,所以我们一般使用一些“更高级”的原语,其中 每个原语都是由机器语言提供的较低级的原语组成的抽象工具。因此,我们可以得到一个概念 上比机器语言更高级的方式来描述算法的正式的程序设计语言。我们将在下一章中讨论这种程 序设计语言。 . 兩 : , • 不 綠 iii 顯轉爹轉:顯 _ II 1 ' 1 , _ I K H H h I H h r H J rH 卜"! "V -- 卜 r n n L i 't ■,‘ ‘ ‘ V f t 1 H b L 才 ■: r H - 卜 卜 " 1 - H I 「 r - , r 1 Jr '.::,、、.;:. : I H H t q r y : L I - |7>1-+ I h I Tl ^ T- h n ht- H |- r T | h 4. H H -r rl H - 11 - I I I r* 1 ,, 1 l l . 1 I .s 1 * 1 |l| t \ h n ,H I H A V M 1 , J 1 M I 'M - . L i M ' L ' 1 ' J i:■■■■~J ■>■■■■■■• : •*••*': *■、.•_ .I.t...Cl •: .. :I...:..._ J : ^ : •- .*. .*. *• - .*. :•: •:•_•:*. .i.vj.-;'. J:' •••.' Ci_.:i •*.* .:. I*. |:| I : ti:' :•: : J*I : :•-;•:. .:'^:. - '.VI:^.:. .:i I:.* • : ^i : *0.1 •: !• •: :•: ^ : :- - : V - V-* . .• •. . ~.: •- "*• . -* -*. :. -: :- w *--J .. - V « *-: > :. -* : ^ ^ .*•>* : :.乂 . :. :.k >' : . : .p :.: : 5.2 算法的表示 139 5.2.2 伪代码 现在我们暂时不介绍正式的程序设计语言,而是介绍一种非正式但更加直观的符号系统, 这个系统称作伪代码。一般而言,伪代码 ( pseudocode ) 是 一 种在算法开发过程中非正式地表 达思想的符号系统。 一种简单地获得伪代码的方法是放松正式语言用于表达最终算法的那些规则要求。这个 方法通常是在已经预先知道目标程序设计语言的情况下使用。这种在程序开发初期使用的伪 代码是由语法-语义结构组成的,这种结构类似于目标程序设计语言所使用的结构,但是不 那么正规。 当然,我们的目标是在不把讨论限定于特定程序设计语言的情况下考虑算法的开发和表示 问题。因此,我们得到伪代码的办法是开发一种一致且简明的用来表示循环语义结构的表示法。 这样一来,这些结构将成为我们表达思想的原语。 这样的循环语义结构之一是保存计算的值。比如,我们计算了日用账户和存款账户上的结 余总和,并打算保存这个结果以便以后引用。在这种情况下,我 们用: 名字—表达式 形式表达,其中,名字是我们欲引用结果的名字,而表达式则描述其结果将被保存的计算。我 们将这个语句读为“把表达式的值赋给该名字”,并且称该语句为赋值语句 (assignment statement ) 。例如,语句 RemainingFunds — CheckingBalance + SavingsBalance 是把 CheckingBalance 与 SavingsBalance 的值相加的结果赋给名字 RemainingFunds 的赋值语句。 这样, RemainingFunds 可以在将来的语句中用于引用该总和的值。 另一个递归语义结构是,根据某个条件的真与假从两个可能的活动中选择一个。这样的例 子有: 如果国内生产总值增长了,那么买进普通 股票; 否则,卖出普通股票。 若国民生产总值增长了则买进普通股票,否则就卖出。 买进或卖出普通股票取决于国内生产总值的增长或减少。 其中每个语句都可以写成符合如下结构的 形式: if (条件 )then (活动) else (活动) 这里,我们使用了关键字 if (如果)、 then (那么)和 else (否则)来指示这个主结构中的不同 子结构,并且使用括号来限定子结构的界限。釆用这种语法结构作为伪代码,我们便得到了可 以表达这种常用语义结构的统一方法。因此,尽管 语句: 根据该年份是否是闰年,总天数相应地被366或365除。 可能会产生一种更富有创造性的文字风格,但是我们将坚持选择简单的 形式: if (年份是闰年) then (总天数一总天数被3阢除) else (总天数一总天数被 365 除) 对于不涉及 else 活动的情况,我们也可以采用较短的语法: 140 第 5 章算 法 if (条件 ) then (活动) 利用这种表示形式,语句 如果处于销售额减少的场合,那么价格降低5%。 将可以简化为: if (销售额 降低 〉 then (价格降低5%) 另一个常用的语义结构是,只要某个条件为真,一个语句或一组语句将重复地执行。这样 的例子有: 只要有票可卖,那么继续售票。 和 当有票可以卖时,保持售票的状态。 对于这两种情况,我们采用下列统一的模式作为伪 代码: while (条件〉 do (活动) 简而言之,这个语句意味着检查 条件, 如果为真,那么实现 活动, 然后返回再次检查 条件。 但是, 如果发 现条件 为假,那么就转到 while 结构后的 下一个 指令。于是,前面的两个语句可以简 化为: while (仍然有票 可卖 ) do (卖票) 缩进通常可以提高程序的可读性。例如,语句 if (未下雨) then (if (温度= 热) then (去游泳) else (去打高尔夫) ) else (看电视) 比该语句的下述格式容易 理解: if (未下雨 ) then (if (温度=热 ) then (去游泳) else (去打高尔夫 》 else (看电视) 所以,在我们的伪代码中将使用缩进。(注意,我们甚至可以利用缩进来调整闭括号的位置,使 得它与对应的开括号对齐,以辨认语句或短语的作用范 围。) 我们想用伪代码来描述那些在其他应用中可能作为抽象工具的活动。对于这样的程序单元, 计算机科学有许多术语,如子程序、子例程、过程、模块和函数等,其中每一个在含义上都有 自己的变化。对于伪代码,我们将采纳过程 ( procedure ) 这个术语,并利用这个术语来给出一 个标题,作为这个伪代码单元的名字。更精确地说,我们将下列形式的语句作为一个伪代码单 元的 开始: procedure 名称 其中, 名称是 该单元特有的名字。在这个引导性语句的后面是一系列定义该单元动作的语 句。例如,图 5-4 是称为 Greetings 的过程的伪代码表示,该过程打印 “ Hello ” 3次。 5.2 算法的表示 141 procedure Greetings Count — 3 ; while (Count > 0) do (打印信息 “ Hello ” ; 并且 Count — Count - 1) 图 5-4 ft 代码形式的过程 Greetings 当一个过程所实现的任务在伪代码其他地方需要时,只需要通过名称来请求它。例如,如果 有取名为 ProcessLoan 和 RejectApplication 的两个过程,那么可以通过下面的语句在 if - then-else 结构内请求它们的 服务: if (• ■ ■) then (执行过程 Process Loan ) else (执行过程 RejectApplication ) 当被测试的条件为真时,执行过程 ProcessLoan , 而在条件为假时,执行过程 RejectApplication 。 如果过程用于不同的环境,设计伪代码时应该使其尽可能通用。一个给名字列表排序的过 程应该设计成能够给任何列表(而不是特定的列表)排序,因此应该按照这样的要求来编写该 过程,即要排序的列表不在过程内部指定。事实上,该列表应在这个过程的伪代码里以类属名 来指称。 在伪代码里,我们将采用这样的 约定: 这些类属名(称为参数)列在括号里,并在标识过 程名字的同一行上。例如, 一 个名为 Sort 的过程(设计成对任何名字列表进行排序)以下列语 句 开始: procedure Sort ( List ) 在后面的伪代码里,在需要引用要排序的列表时,就可以使用类属名 List 。 同样,当需要 Sort 服务时,我们将知道是什么列表要替代过程 Sort 的参数 List 。 于是根据需要,可以 写成: 把过程 Sort 应用在机构成员列表 和 把过程 Sort 应用在婚礼来宾列表 南 fe 满賛言资,慧使■多今单_的名森, 魏知 $〜雄(辦辱说”或 寒每樣在。个縣奉■:德祿多顯麟轉律’ : 讎隱 獅涵 _ 嚷 纖'纖雜麟 ■■ —槪癖与春錢本働■樣狐:•灘漏麻 1 1, ■帽 :麵呼譯_琴_釋 Bs tixna . t .0 dA^r r ■.攀:如珠餐样 冬本? — 辦奉择芦_龜蓄:坪相户:中,秦濟行> 禅表的屬參 舞备 (餘 ( camel casing ), :该;捧式有字灰小写奸,其锋与 hsc 每样式'相同 -, ^ estimated & rivalTime , 本书中,我们使用 Pascal 样式,但这种选择很大程度上是个人偏好。 142 第 5 章算 法 记住,伪代码的目的是要提供一种用可读的、非形式的方法来表示算法。我们希望符号系 统能够帮助我们表达思想,而不受制于严格的形式规则。因此,在需要的时候,我们可以随意 扩充或修改伪代码。特别是,如果 一 组括号中的语句又涉及带括号的语句,会使括号配对很困 难。在这些情况下,许多人发现在闭括号后面加上简短的注释,说明是哪个语句终止了,是非 常有帮助的。特别是,在 while 语句的闭括号后面可以加上 end while , 产生下列形式的语句: while (...) do )end while 或者 while (...) do (if (...) then (. )end if )end while 其中我们已经指明了 if 和 while 语句的结束。 我们的目的是用一种可读的形式来表达算法,因此可以随时引进一些直观的辅助工具(缩 进、注释等)来达到这个目的。其次,如果碰到了一个尚未在我们的伪代码中体现的递归问题, 那么可以选择扩充伪代码,采用一致的语法来表示这个新的概念。 间题与练习 : - ■ .. - .•...... » ■ -.二- .... ■- _ .-- _ 一 . . ••二 :- .. - ~ : 一 二 . .、 , s v: _ • •- - ,... -• l -. . 一 但是它輪为机麟的一个兮^娜_ ;给贈 3. 減纖法給.禱___雜 麵鱗蘇 爾数除以截小的巍,并且播除数系^数分疏賦给料^ ( 成知廉 魏^太杂_數 h : 鄭姨我码蘇方 个算法 。 ;\ . A ' , t v . 一― 4. g 述一不在计算胡;程序设计以外的学科里角的康语集合。 > ; : / 二 5.3 算法的发现 程序开发由两个活动组成——发现潜在的算法和以程序的方式表示算法。从这点看,我们 一 直在关注算法表示的问题而未把算法是如何被发现的问题放在首位,但是算法的发现在软件 开发过程中往往是更加具有挑战性的步骤。毕竟,发现一个算法来解决问题需要找到一个解决 该问题的方法。因此,要理解算法是如何发现的就是要理解问题的求解过程。 5.3.1 问题求解的艺术 问题求解的技术和学习更多相关知识的需求并不只存在于计算机科学中,这是一个几乎在 任何领域中都永久存在的问题。由于算法发现的过程和一般问题的求解过程之间存在着紧密的 联系,使得计算机科学进入了那些试图寻找更好的问题求解方法的学科中。最终,我们希望可 以把问题的求解简化为一个算法,但是己经证明这是不可能的。(这是第12章相关内容的结果, 5.3 算法的发现 143 在第12章我们将展示有些问题是找不到算法解决方法的。)因此问题求解能力更多地成为一种 有待开发的技艺,而非需要学习的精确科学。 作为问题求解难以琢磨、颇具艺术性的本质的证据,数学家波利亚 Polya ) 在1945年 列出了以下非严格定义的问题求解阶段,其蕴涵的基本原理直至今日仍然是很多人教授问题求 解技能的基础。 第1阶段理解问题。 第2阶段设计一个解决问题的计划 。 第3阶段完成计划。 第4阶段从准确度及其是否有潜力作为一个解决其他问题的工具这两方面来评估这个 计划。 我们把上述阶段移植到程序开发的语境中,这些阶段变成: 第1阶段理解问题。 第2阶段寻找一个可能解决问题的算法过程的思路。 第3阶段阐明算法并且用程序将其表达出来。 第4阶段从准确度及其是否有潜力作为一个工具解决其他问题这两方面来评估这个程序。 在描述完波利亚的观点后,我们应该着重强调这些阶段并不是在尝试求解问题的时候需要 遵循的步骤,而是在求解过程中有时需要完成的阶段。这里的关键词是“遵循”。仅遵循这些步 骡是不能求解问题的,要求解问题,必须有创新精神和领先一步的意识。如果在求解问题的时 候总是抱有“现在我完成了第1阶段,该是开始第2阶段的时候了”之类的想法,那么你可能根 本不能成功。然而,如果仔细考虑问题并且最终解决了它,可以回想在解决问题的过程中你都做 了些什么,并且可以发现确实经历了波利亚所描述的各个阶段。 波利亚阶段原理的另外一个重要观点是并不一定要按顺序执行这些步骤。成功的求解问题 者通常是在完全理解问题本身(阶段 1) 之前就开始设计构想解决问题的策略(第2阶段)。然 后,如果他们的策略失败了(在第3阶段或者第4阶段),这些人会对这个问题的复杂程度有更 深的理解。基于这些比较深入的理解,他们会回过头去构想另一个更有希望成功的策略。 必须记住的是,我们正在讨论怎样求解问题——并不是我们希望问题如何解决。在理想情 况下,我们希望消除前面描述的“尝试-错误”过程中的固有的浪费。在开发大型软件系统的情 况下,如果在像第4阶段这样晚的时候才发现问题,那么就会导致资源的极大浪费。避免这样 的灾难是软件工程师的主要目标(第7章),他们习惯于在寻求解决方案之前,坚持对该问题有 一个全面透彻的理解。当然,有些人可能会说,在一个问题解决之前是不可能真正理解这个问 题的。 起码事实上,问题无法解决表明缺乏对问题的理解。因此,坚持在提出任何解决方案之 前必须对问题有完全理解的想法看起来有些过于理想化。 作为一个例子,我们考察下列问题: 曱承担了确认乙的3个孩子的年龄的任务。乙告诉曱3个孩子的年龄乘积是36。在 考虑了这个线索以后,曱要求乙给出另外的线索,于是乙告诉甲3个孩子的年龄之和。 甲再次要求乙给出其他线索,乙告诉甲他的最大的一个孩子弹钢琴,在得到这个线索 之后,曱得到了乙的3个孩子的年龄。 乙的3个孩子的年龄分别是多少? 乍一看,最后一个线索与问题完全没有关系。但是显然,正是因为这条线索,甲最后确定了 3个 孩子的年龄。这是为什么呢?让我们制订一个计划并且遵循这个计划,尽管我们对于这个问题 还有很多疑问。我们的计划是跟踪问题陈述所描述的步骤,同时在这个进程中记录对甲有用的 信息。 144 第 5 章算 法 第一条线索告诉甲,3个孩子的年龄之积是36。这意味着表述3个年龄数值的三元组肯定 是图 5-5 a 中列出的三元组之一,第二条线索是期望的三元组内3个数字之和。我们并不知道这 个和到底是多少,但是知道这个信息并不足以让甲得到正确的三元组,所以期望得到的三元组 的和在图 5-5 b 中的表里面至少出现两次。此处只有(1,6, 6) 和(2, 2, 9) 具有相同的和,这两组数 字的和均是13。当给出最后一条线索的时候,我们最终理解了最后一条线索的重要性。这个信 息与弹钢琴本身没有什么关系,而是说明了只有一个孩子年龄最大的事实。这条线索将三元组 (1,6, 6) 排除并且最终得到结论,就是3个孩子的年龄分别是2、2和9。 (1,1,36) (1,6,6) 1 + 1 + 36 = 38 1 + 6 十 6 = 13 (1,2,18) (2,2,9) 1+2 + 18 = 21 2+2+9= 13 (1,3,12) (2,3,6) 1 +3 + 12 = 16 2 + 3 + 6 = 11 (1A9) (3,3,4) 1 +4 + 9= 14 3+3+4= 10 ⑻乘积为36的三元组 0>) ( a ) 中每个三元组的和 图 5-5 计算过程 在这个例子中,直到我们尝试实施解决问题的计划(第3阶段)的时候,才获得对这个问 题的完全理解(第1阶 段)。 如果坚持要首先完成第1阶段,我们可能根本得不到问题的答案。 这种解决问题过程中的不规则性是开发问题求解的系统方法的基础。 另外一个不规则性在于,那些还没有得到明显成功的问题解决者可能在完成其他任务的时 候突然得到的神奇灵感,并发现原来问题的一个解决方法。 H . von Helmholtz 早在1896年就发 现了这种现象,并且数学家庞加莱 (Henri Poincad ) 在巴黎对心理学会的一次演讲中对此进行 了讨论。在这个演讲中,庞加莱叙述了他在解决一个问题时的 经历: 他将原来的问题放在一边 儿,开始做其他工作之后,却突然意识到原来问题的一个解决方法。这种现象反映出这样一个 过程,大脑的潜意识部分好像一直在思考问题,如果成功,便会把解决方法反映给大脑的有意 识部分。今天,我们把在对于问题的有意识的工作与突然的灵感之间的这个时期称作沉思期, 对于这个时期的理解仍旧是当前研究的目标。 5.3.2 入门 前面,我们已经从一些心理学的观点讨论了问题求解,但是回避了直接对质这样的一个问 题,即应该如何求解问题。当然有很多问题求解的方法,每一种方法都可能在某些场合获得成 功。我们将简要地介绍其中一些方法。 目前, 我们注意到这些技术中贯穿着一条普遍的线索, 简单地说就是要“入门”。作为一个例子,让我们考虑下面这个简单问题。 在甲、乙、丙和丁进行赛跑之前,他们分别对结果进行了 预测: 甲预测乙将会获胜; 乙预测丁将是最后一名; 丙预测甲是第 三名; 丁预测甲的预测将是正确的。 这几个预测只有一个是正确的,并且是最后的获胜者作出的预测,请据此给出甲、乙、丙、丁 赛跑的名次排序。 在阅读了这个问题并且对数据进行分析之后,我们很快就可以认识到,因为甲和丁的预测 是等价的,而只有一个人的预测正确,所以这两个预测都是错误的。因此甲和丁都不是胜利者。 在这一点上我们己经为解决这个问题迈出了第一步,并且发现获得完整的解决方法的过程仅仅 5.3 算法的发现 145 是以此为基础进一步扩展知识。如果甲的预测错误,那么乙也不是胜利者。这样就只剩下了一 个选择,就是丙是胜利者。因此,丙的预测是正确的。从而,我们知道甲是第三名。这就意味 着最后的比赛名次是丙、乙、甲、丁,或者丙、丁、甲、乙,但是前者被排除了,因为乙的预 测肯定是错误的。因此最后的顺序是:丙、丁、甲、乙。 当然,知道怎样进入问题并不等于知道如何去做这件事。得到立足点,并认识到如何把对 于问题的初始介入扩展为问题的解决方法,要求问题解决者有创意。对于如何入门波利亚和其 他人提出了很多通用的方法,其中一个是反方向解决问题。比如,如果问题是找到对于一个已 知输入产生一个特定输出的方法,我们可以从输出开始,然后反向推导出输入。这个方法就是 人们在解决本节前面的学习叠纸鸟的基本思路。我们把一个已经完成的纸鸟拆开来,然后看看 如何能将它折好就可以了。 另一个通用的解决问题的方法是寻找一个相关的、解决起来较简单的并且在此以前已经得 到解决的问题,然后尝试把这个解决方法用到当前问题中。这个技术在程序开发中特别有用。 通常,程序开发并不是解决一个问题的一个特定实例,而是要寻找一种适用于求解一个问题的 所有实例的一般算法。更加准确地讲,如果面对一个要把姓名列表按照字母排序的程序开发任 务,我们的任务并不是只给一个特定的列表排序,而是寻找一个可以用来给任何名单排序的通 用算法。因此,尽管指令 交换名字 David 和 Alice 。 将名字 Carol 移至 ! J Alice 和 David 之间 将名字 Bob 移到 Alice 和 Carol 之间。 可以把由 David 、 Alice 、 Carol 和 Bob 组成的名单正确排序,但这并不是我们需要的通用的算法。 我们需要的算法应当既可以为这个名单排序,又可以为其他名单排序。这并不是说为特定列表 排序的算法在我们研究通用算法的过程中是完全没有意义的。例如,我们可以通过考虑特殊的 情况来进入问题,来寻求能够用于开发通用算法的一般原则。于是,在这种情况中,解决问题 的方法可以从对于多个相关问题的解决中得到。 另外一个进入问题的方法是逐 步求精 ( stepwiserefinement ), 这种方法本质上不是试图立即 解决整个问题,而是首先把一个手头的问题看做多个子问题。我们可以按照步骤通过解决各个 子问题来最后解决整个问题,其中每一步都比解决完整的问题要更容易。逐步求精的方法还可 以把这些步骤划分成更小的步骤,然后这些更小的步骤还可以继续进行划分,直到整个问题被 简化为一组简单的子问题为止。 从这点看来,逐步求精是一种 自顶向下方法 ( top-down methodology ), 这种方法从一般发 展到特殊。相反, 自底向上方法 ( bottom-up methodology ) 是从特殊发展到一般。尽管理论上 相反,但是实际上这两种方法在应用中互为补充。比如,逐步求精的自顶向下方法分解问题通 常是由那些可能从事自底向上工作的解决问题的人指导的。 逐步求精的自顶向下方法从本质上讲是一种组织工具,这种工具解决问题的属性是组织方 式的结果。逐步求精早已成为数据处理中一个重要的设计方法,其中大型软件系统开发项目拥 有一个很大的组织模块。但就像我们将要在第7章中学习的,大的软件系统越来越多地通过结合 预先编制的部件完成(本质上是一种自底向上的方法),因此自顶向下和自底向上的方法仍然是 计算机科学中重要的工具。 之所以维持如此宽广的观点是基于以下的一个事实,即将事先形成的观念和预选的工具带 入问题求解任务中,有时候可能掩盖问题的简单性。本节前面讲的求解3个孩子年龄的问题就是 这种现象的一个很好的例子,学习代数的学生解决问题时总是给出系统的联立方程,这种方法 可能将问题带入死路,而且常使问题解决者误认为并没有足够的信息来解决问题。 另外再给出 一 个类似的 例子: 当你从码头走上船的时候,帽子掉进了水里,但是你并不知道。河水的流速是 2.5 英里 ®/ 小时,所以帽子开始向下游漂去。同时,你开始以相对于水流 4.75 英里/小时的 速度向上游前进。10分钟后,你发现帽子不见了,然后调转船头,开始追你的帽子。 试问多长时间可以找到帽子? 大多数学习代数的学生还有那些热衷使用计算器的人在解决这个问题的时候,首先会确定 船在10分钟后向上游行进了多远以及在相同时间内帽子向下游漂了多远。然后,他们确定船将 使用多少时间赶上帽子。但是,当船到达这个位置的时候,帽子又向下游漂了一段距离。因此, 问题解决者要么就是用微分的方法重新解题,要么就陷入到计算每次船到达帽子的上一个位置 的时候帽子所处的位置这样一个怪圈里。 其实问题很简单。这里的失误在于解题者忙于列出公式并进行求解。其实,我们需要先将 技术放在一边,并且调整对于问题的观点。整个问题发生在河中。事实是水相对于河岸的流动 与解题是不相关的。想象一个相同的问题发生在传送带上而非水中。首先,在传送带停止的情 况下解决问题。如果你站在传送带上,然后把帽子放在脚下,之后反向行走10分钟,那么返回 到帽子所在处需10分钟。现在启动传送带,这意味着旁边的场景将相对于传送带反向运动起来。 但是,因为你站在传送带上,这就不会改变你和传送带或者帽子之间的相对关系,所以仍会使 用10分钟回到放帽子的地方。 我们可以得到这样一个结论,算法的发现仍旧是一种富有挑战的艺术,这项工作必须花费 一 定时间才可以完成,而不能像一门由明确的方法组成的学科那样学到。因此,机械地训练未 来的问题解决者使之遵循一定的方法,就是在压制那些本来应该被培养出来的创造性技能。 问嶷与练# 1. a. 寻找二个算法求解下面的 问题; 已知一个正整数 m 寻找一个正整_表,._表中所有正整数的 乘积是其正整数和为 w 的所有正整数列霉申最大的。例如,如果《为4,那么 p 求的列表由两个2组 成,_为2父2大于 1X1X1X1、1X1X2_1X3。 如果《为5,则所求的列表|2和3组成。 b. 如_^)01,那么所求的列表由哪些数字组成? c. 说明# 0J 何“入门”这个问题的。 2. a. 假谗:$知跳棋祺盘由 2 B 行和每行 2" 列组成,给定正 整数私 以及一禽 L 型棋子,每一个都恰好可以 覆的3个$方形格于。如東任何一个格子从祺盘上被切掉,麵 ] 是否^可以用遺些棋子在既 : 不叠,又不跨越棋盘边 M 件下把剩余的棋盘填满? b. 请说_样用问题 3 的解来证明:对于所有的芷整数 m 2夂1 是可以被3除尽酚。 c. 说明翁题_何题 b 与波利並的求解_的关联性。 -: 3. 解码下面的消息,并解释你祥如何入门的■。:- , 4. 如巢知算解决挵图游 stig 图片抛散在桌面 is 后袷其# f 出‘整圈,你会采用自顶向下的方法 吗?如_看着拼图盒上的完整图来做,你_?回答会改变吗? " 5.4 迭代结构 我们现在要学习一些在描述算法过程中使用的重复结构。在本节中,我们讨论迭代结构 ①1英里 =1.6093 千米。——编者注 5.4 迭代结构 147 (iterative structure ) 0 在这种结构中,一组指令以循环方式重复执灯。在 5.5 中’我们将介绍 递归技术。作为一些关联知识,我们将介绍一些流行的算法一顺序搜索法、二分搜索法和插 入排序法。我们从介绍顺序搜索法开始。 _篆比计拜幕 科学事 _个世蛘《已经开始使用和编舅氣代结碱心实际=首歌的 i ,;!' 1 ' 1 I ".' i : … liiP I'!' N 1 , 、:㈣下一癌、: __ - ■ .. . . ..-i, - ; : ^麵唱和声: . :此外,乐满::' - , ■ ; _ -i : ... I . :卜. . 巧-•輕十 V 兮故耶- • 忠:,在 查彘;3键廉铉缔彳 i!ii;Si;::; Si:':!: s'me 〜 :搬 ^ 只不僉式: .• N ^- l ; . ••- - ;; c:':;:- ,- .. — -jj 'u .... -r- . • . :;: ::; ..q':. >w.w-;--"r-:T ••••.:-. : - r-" ' : r : T=~f i . 1 " f "!, ■ t .: : »' i 1 .:f '■' ! :!'.!:• 1 ' 'm : 1 ' ' '•:■: : •; :; ■::::: i.j-W •:.,:!':!;, ,i : "I,::,- . J; : ..i ... i ;.:! . !: . : | '..v;' ,.: •;' -_L •,. : '_;L _:L __ ■I'..'. ;! | 1,. : . _i . : .1 fi;:.. fMmdmmmMmmm : fMB:. u mm ;;:..:x : i'.r' l '-:|:":;:| ! i rr ::.-■ t :i ':■:■!; ' rfv !:''':::;i-V*:: .J: .V: ST ^:: : i : :; : ! ::^!' : ii ' : :. ::■ .■ •: : i • • _. ! : ;.;!; .,1- .. .; .::.... i , : • 1 Sit;: !;!: ;: : ii|v ; ; ; E ' V : :;: : : i ; ii ^ 糾 W ; -3;: !•' ;i :! 'i ;' ,! l 'ij l :, : ;,:::; i , I •:• ' : :|'rv;j ! : ; >o^'Kjii;^ r :!.^'j i.'j !,: 1 !: ;: j :,;;: •:^:^v:vvii!;;!;-:i: I:. ; ^ : '^'::X::,.: r |:^,:y:: :,.: ,!: •: .: !•.!;i':; 1 : ::!;:: ::;:: ;: : 八 ! 哪. ; • : :i I 5.4.1 顺序搜索法 考虑一下查找某个特定值是否存在于一个列表中的问题。我们希望开发一个算法来确定这 个值是否在列表中。如果它在列表中,我们认为查找成功,反之则认为查找失败。我们假设被 查找的列表依照某种规定已被排序。例如,如果这是一个姓名列表,我们假设列表中的名字是 按照字母顺序排列的;如果这个列表由数字组成,我们假设里面的表项按照增序排列。 为了入门,我们想象如何在一个大概有20条记录的来宾列表中寻找一个特定的姓名。在这 种情况中,我们可以从头开始扫描整个列表,将每一条记录与目标姓名进行比较。如果找到了 目标姓名,那么查找就将以成功终止。当然,如果到达列表的最后仍然没有找到目标,查找就 以失败告终。实际上,如果到达了(从字母顺序方面看)大于目标姓名的值还没有找到目标姓 名,那么我们的查找就已经宣告失败。(记住,列表已经按照字母顺序排列,所以到达一个大于 目标姓名的表项就意味着目标不可能在列表中出现了。)概括来说,我们粗略的想法是只要还有 姓名没检查而且目标姓名大于正在检查的表项,查询就将继续。 在伪代码中,这个过程可以表 达为: 选择列表中的第一个表项作为 TestEntry while ( TargetValue > TestEntry 并且还有表项没有检查) do ( 选择列表中下一个表项作为 Test Entry ) 如果要终止上面的 while 结构,则两个条件中有一个必须 为真: 或者目标值被找到,或者目标 148 第 5 章算 法 值不在列表中。在任何一种情况中,我们都可以通过比较测试表项 ( TestEntry ) 和目标值来发 现查找是否成功。如果这两个值相等,那么查找就成功了。因此,我们把下面一些语句添加到 以上伪代码例程的 下面: if ( TargetValue = TestEntry ) then ( 宣布查找 成功) else ( 宣布查找失败) 最后,我们观察这个例程的第一条语句。这条语句把列表中的第一条记录选做测试表项, 这种选择基于表中至少有一条记录这样的假设。我们可能认为这是一种安全的猜测,但是为了 保险起见,我们将前面的例程作为下面语句中的 else 部分: if ( 列表空) then ( 宣布查找失败) else (...) 这个过程的伪代码如图 5-6 所示。注意,这个过程可以在其他过程中通过下面的语句加以 使用: 运用过程 Search 于旅客列表查找名为 Darrel Baker 的旅客 这个语句可以查明 Darrel Baker 是否是一名旅客,而若用语句: 利用 nutmeg 作为目标值运用过程 Search 于原料列表 则可以查明 nutmeg (肉 豆寇)是否出现在原料列表上。 procedure Search ( List,TargetValue ) if ( List 空) then ( 宣布查找失败) else ( 选择列表中的第一个表项作为 TestEntry ; while ( TargetValue > TestEntry 并且还有表项没有检查) do ( 选择列表中下一个表项作为 TestEntry ) ; if ( TargetValue ^ TestEntry ) then ( 宣布查找成功) else ( 宣布查找失败) )end if 图 5-6 伪代码形式的顺序搜索算法 概括来讲,图 5-6 所示的算法按照表项在列表中的出现顺序进行查找。因此,这个算法称 作顺序搜索 (sequential search ) 算法。因为其简单,所以顺序搜索法经常用于在较短的列表中 进行查找或因其他方面的考虑需要使用它的情况。在比较长的列表中,顺序搜索就没有(我们 将要学习的)其他技术有效了。 5.4.2 循环控制 一 条指令或者一系列指令的重复使用是一个很重要的算法概念。一种实现这种重复的方法 是称作循环 ( loop ) 的迭代结构。这种结构中,一组称为循环体的指令在某些控制过程的指引 5.4 迭代结构 149 下重复执行。 一 个典型的例子就是图 5-6 所示的顺序搜索算法。这里我们使用 while 语句来控制 以下单条语句的 循环: 选择列表中下一表项作为 TestEntry 实际上, while 语句: while ( % r¥r ) do ( 循环体) 就是循环结构的一个例子,它的执行所跟踪的循环模式为: 检查 条件, 执行循环体, 检查条件, 执行循环体, 检查条件。 直到条件为假。 作为一个普遍规则,循环结构的使用使程序得到了比仅仅将循环体重写多次更高的灵活度。 例如,执行下面的语句3 次: 加一滴硫酸 等价于语句序列 加一滴硫酸, 加一滴硫酸, 加一滴硫酸。 但是我们写不出与下面的循环结构等价的具有相似结构的 序列: while ( pH 值大于 4 ) do ( 加一滴硫酸) 因为我们事先不知道需要滴入多少硫酸才合适。 现在让我们进一步考查循环控制的组成。你可能认为这一段关于循环结构的部分并不重要。 毕竟,通常都是循环体在实际执行手头的任务(比如,加几滴硫酸)——控制活动看起来仅是 相关的开销,因为我们选择在重复的形式中执行循环体。但是,经验表明循环控制是循环结构 中一个非常容易出现错误的部分,所以很值得我们注意。 循环控制由初始化、测试和修改(如图 5-7 所示 ) 3个活动组成,其中每一个活动都决定了 循环控制是否能够成功。测试活动有责任通过查看表明应终止的条件来终止循环过程。这个条件 就是终 止条件 (termination condition )。 为了这个测试活动,我们在伪代码的每 一 个 while 语句中 都提供一个条件。在 while 语句中, f 循环体必须在阐明的条件下执行-终止条件就是 while 结 构中出现的条件的对立条件。因此,在语句 while ( pH 值大于4 ) do ( 加一滴硫酸) 初始化: 设置一个初始状态,这一状态将被修改直至终止条件 测试:比较当前状态和终止条件,如果相等则终止循环 修改: 修改状态使之可以达到终止条件 图 5-7 可重复结构控制的组成 150 第 5 章算 法 _ 中,终止条件是 “ pH 值不大于4”,在图 5-6 所示的 while 语句中,终止条 件是: (目标值^测试项)或(不再有要检查的表项) 循环控制中的另外两个活动确保了终止条件最终可以出现。初始化步骤建立了一个开始条 件,修改步骤将这个条件移向终止条件。比如,在图 5-6 中,初始化发生在 while 语句之前的语 句中,该处当前的测试表项被设定为列表的第一条记录。在这个例子中,修改步骤实际上是在 循环体内完成的,在循环体中,我们将测试位置(也就是测试表项)向列表的末尾移动。因此, 在执行完初始化步骤后,修改步骤的重复执行最终使得程序可以到达终止条件。(或者到达一条 大于或等于目标值的测试项,或者到迖列表的末尾。) 我们应该強调的是初始化和修改步骤必须导致合适的终止条件。这个特性对于正确的循环 控制至关重要,因此在设计循环结构的时候必须仔细检查它是否存在^如果没有进行相应的检 查,在最简单的例子中都会发生错误。 一 个典型的例子 就是: Number — 1; while (Number ^ 6) do (Number — Number +2) 这里,终止条件是 “ Number =6”。 但是 Number 初始化为1,并且每次修改的时候都加2。因此, 在循环过程中 Number 的值将是1、3、5、7、9等,但是永远不会是6,这样,循环就无法终止。 循环控制部件的执行次序可产生微妙的结果。事实上,有两种常用的循环结构,它们仅仅 在循环控制部件执行次序上有所区别。第一个如以下伪代码语句 所示: while ( 条件 ) do ( 活动) 这个结构的语义由图 5-8 中的流程图 ( flowchart ) 给出。(流程图使用各种形状来表达单个步骤 并且用箭头表达步骤的顺序。线框形状之间的不同表示相关步骤涉及的动作类型不同,比如菱 形表示判断,而矩形表示任意语句或语句序列。)注意, while 结构的终止测试出现在循坏体执 行之前。 相反,图 5-9 中所示的结构要求循环体在终止条件测试之前执行。在这种情况下,循环体 总是至少执行一次,然而在 while 结构中,如果终止条件在第一次测试时就满足,则循环体一 次都不用执行。 图 5-8 while 循环结构 图 5-9 repeat 循环结构 我们用语法 格式: repeat ( 活动 ) until ( 条件) 5.4 迭代结构 151 在伪代码中表示图 5-9 所示的 结构。 因此,语句: repeat ( 从你口袋里面取出一个硬币) until ( 你口袋里没有硬币) 假设开始时在你的口袋中至少有一个硬币,但是下面的语句: while ( 你口袋 里面有硬币 ) do ( 从你口袋里取出一个硬币) 就不再限定必须至少有一枚硬币。 依照伪代码的术语,我们通常会把这些循环称作 while 循环结构或者 repeat 循环结构。在 更一般的情况下,有时候我们用前测试循环 (pretest loop ) —词来表示 while 循环结构(因为终 止条件的检查是在执行循环体之前执行的),而把 repeat 循环结构称作后测试循环 (posttest loop ) (因为终止条件的检查发生在执行循环体之后)。 5.4.3 插入排序算法 作为另外一个迭代结构的例子,下面考查将一个姓名列表按照字母顺序进行排序的问题。 但是在继续介绍之前,我们应该了解排序的限制。简单来讲,我们的目标是在一个列表的内部 将表项进行排序,即通过把表项移来移去来将列表排好序,而不是把列表移到其他位置。我们 的情况类似于这样的列表排序问题,每个表项记录在一张索引卡片上,卡片分散在桌面上,把 桌面挤得满满的。我们已经清理出足够的空间来放这些卡片,但是不允许挪幵其他东西来挤出 更多的空间.这种限制在计算机应用里是很典型的,其原因当然不是计算机里的工作空间一定 像桌面那样拥挤,而仅仅是因为我们希望更有效地利用存储空间。 让我们从考虑如何在这样一个桌面上进行排序来“入门”。考虑一个名字 列表: Fred Alex Diana Byron Carol 一个方法是每次只对这个表中的一个子表进行排序。 Fred 在表的最顶部,现在只看他和 Alex 。 因此我们只要拿出包含姓名 Alex 的卡片,将 Fred 放到它留下的空缺处,然后把 Alex 放到 Fred 原来的位置,如图 5-10 第一行所示。这样名字列表就变 成了: Alex Fred Diana Byron Carol 现在,顶部的两个名字构成了一个已经排序的子列表,但是最顶部的3个名字还不是 。因 此,我们可以取出第三个名字 Diana , 然后将 Fred 移动到 Diana 拿走后留下的空白处,然后将 Diana 放入 Fred 原来所在的位置,如图 5-10 中第二行所示。最顶部3条记录己经排好序了。继 续这个操作,通过取出第四个姓名 Byron , 然后把 Fred 和 Diana 下移同时将 Byron 插入空缺处, 就能够获得最顶部4个记录均被排序的列表了(参见图 5-10 的第三行)。最后,我们通过取出 Carol , 然后把 Fred 和 Diana 下移,同时把 Carol 放入空缺来完成排序工作(参见图 5-10 的第 四行)。 初始列表: 图 5 _10按字母顺序为 Fred s Alex 、 Diana 、 Byron 和 Carol 排序 在分析了一个特殊列表的排序过程之后,现在的任务是将这个过程通用化,以获得对一般 列表排序的算法。为了达到这个目的,我们考查图 5-10 中的每一行,发现它们都执行同样的通 用的 过程: 取出列表中还未排序的部分中的第一个名字,将已经排序的部分中大于此名字的表 项向后移,然后将这个取出的名插入到这一部分的空缺处。如果把取出的名字称为主元 ( pivot ), 那么这个过程可以用下面的伪代码表示: 把主元表项移到一个临时位置使该列表留出一个空 位置; while (如果这个空位置上面存在一个名字并且那个名字比主元大) do ( 把这个名字向下移到空位置上使该名字上面留出一个空位置) " 把主元项插到列表的空位置上 下一步,我们来观察这个过程怎样被重复执行。为了开始排序过程,主元项应该是列表 的第二项。然后每当再执行一次前,主元项的选择应该是从列表中的下个位置直到列表的最 后一个位置。也就是说,随着前面的程序的重复,主元项的位置从第二项移进到第三项,然 后到第四项,依次类推,直到程序定位到表的最后一项。我们使用下面的语句对这个过程进 行 控制: 5.4 迭代结构 153 N — 2; while ( N 的值不超过列表的长度 ) do ( 把列表的第 N 项作为主 元项; (N - N +1) 这里, N 表示主元项的位置,列表的长度是指列表中的项个数,而省略号则是指放置前面例程 的位置。 完整的伪代码程序如图 5-11 所示。简言之,这个程序通过重复地移动表项并且将其插入适 当的位置来完成排序。由于这种重复的插入过程,这种算法 称为插入排序 (insertion sort )。 procedure Sort ( List ) N — 2; while ( N 的值不超过列表的长度 ) do ( 把列表的第 N 项作为主元项; 把主元项移到一个临时位置使该列表留出一个空位置; while ( 如果这个空位置上面存在一个名字并且那个名字比主元大 )do ( 把这个名字向下移到空位置上使该名字上面留出一个空位置) 把主元项插到列表的空位置上; 图 5-11 用伪代码表达的插入排序算法 注意,图 5-11 中所示的结构是一个循环内部还有循环的结构,外层循环用第一个 while 语 句表述,而内层循环用第二个 while 语句表述。每次执行外层循环体都导致内层循环体被初始 化而且重复执行直到终止条件出现,因此外层循环体的一次执行将导致内层循环体多次执行。 外层循环控制的初始化部分通过使用赋值语句 N —2; 实现。修改部分通过每次给 N 加1 ( 语 句 N — N +1) 完成。终止条件当 N 的值超过列表的长度时出现。 内层循环控制通过移动主元项并且创建一个空缺位置来初始化。循环的修改步骤通过移动 表项到空位置来实现,因此导致空位置上移。终止条件在空位置上方紧临的名字不大于主元或 空位置到达列表顶部的时候出现。 1•雀__摘顺雜縁•■赫 用録排 雜趣表 -:- • - - , ■ ‘ - ■ ■ - -_, - 、 ■ . - - • —• • - . • .-••••- ••- ■:义 .:•:-.. - - • : ..T7^-T-. . - : .• •• . .:- • . - •: J.: - -.0 •V. •• X - X +1). . --- . -• •• _ • . I.;::- .. .. ”0... . : ..-•7 -r±. 3,.今 流管的 程序设甘语言釋興请 法:: _ _ _________ 絲示俞测域循环 v 而用雛\ - -!:• 8_獄議1 |;J. ^ ' ' I〆 二 V. 'III : '.jl':!:.::'- oi : ■ ri ,: 聲货 If: 逆界 i_: : :'-.r •!:::.! : ! : ,r;:;- _丨 CtlVH ll I., I|W hJ ' Jh ' j,1 f, r" ' u,- 154 第 5 章算 法 do (…) while (…) 表示后测试循环。尽管设计上显得很优雅,但是这种相似的形式会导致什么问题? 4. 假设图 5-11 所示的猶入棑序算法用来对表 Gene 、 Cheryl 、 Alice 和 Brenda 进行排序。描述外层 while 结 构的循环体的每次执存结束时表的 枸成。 5. 为什么:不能 把:: fflfU 中 whi 化谭 句内的 “大于”改为“大于等干”? 酬___義麵議曝 : : : .,,';:•• ' !l in.:! ;•; li '| .! . . i: . . !.. . . .1 . ;• ' 1 I ':i J :: : : 1 1 面齐始,然痕选择 表中的 剩余项中最小的项,同时将其放到表的集二个位置。逋过童复从列表的剩余 部分选择并前移最小的项,经过排序的部分便从前向后逐渐变长,而后面未排序的部分逐渐缩短。使 用我们的伪代码来_用选择排序法实现的类似于图5,11中的列表排序过程。 7. 另一个著名的排序算法是冒 泡排序 (bubble sort )。 这个算法基于这样一种机制,重复对列表中相邻的 两项进行比较,如果玄们并不是依照规定排序的就交换它们的位置。我们假设等待排序的列表有《项。 冒泡排序法将从比较(并可能互换位置)第《项和第 《_1 项开始。然后,它蒋考虑第1项和第《-2项 之间的关系,并且继续向列表的前面移动,直到列表的第一项和第二项进行比较(并可能互换位置)。 可以看出,通过一遍排序,最小的表项将被移到列表的最前面。同理,再一次排序将把仅大于最小表 项的项放到列表的第二个位置。因此,重复 《-1 遍后就完成了整个列表的排序。(如果我们观察算法 的工作过程,那么就会看到小表项像气泡一样冒到了列表的前面。)请使用伪代码来表达用冒泡排序 法实现的类似于图541中的列表排序过程。 • J - - — ' - : _ 一 . • .... — ... • , • - _ TT -—. - 5.5 递归结构 _ I _ 递归结构提供了除循环模型以外用来实现重复活动的另外一种选择。循环涉及重复一个指 令集,其方式是执行完成一组指令,然后重复执行,而递归则是通过将指令集作为自身的一个 子任务重复调用来运行的。在处理来电的过程中,呼叫等待的特性就是一个很好的递归例子。 在这个例子中,当处理另外一个来电的时候,先前未完成的通话将被搁置一边,结果是一共进 行了两次通话。然而,这两次通话并不是以那种先执行一个然后再执行一个的类似于循环结构 的方式完成的,而是一次通话在另外一次通话过程中进行。 5.5.1 二分搜索算法 作为一种介绍递归的方法,让我们再次处理在一个已经排序的列表中查找是否存在某特定 项的问题,但是这次将采用查字典时所使用的方法来考虑这个问题。在这个例子中,我们不再 按照一项一项或者是一页一页的顺序进行,而是通过直接翻到我们认为目标可能存在的那一页 开始工作,如果足够幸运将可能一下就找到目标,否则就必须继续查找。但至少我们已经大大 缩小了查找的范围。 当然,在查字典的时候,我们有知道可能在哪能查到单词的先验知识。例如,查 somnambulism 这个单词,我们就会从字典的后面部分开始查找。但是对于一般列表,我们并没有这种先验知 识,所以我们总是假定从列表的“中间”项开始查询。这里“中间”这个词之所以被引号括起 来,是因为一个列表可能包含偶数个项,那么此时就没有准确意义上的中间项了。在这种情况 下,我们将假定该“中间”项为该列表中后半部分的第一项。 如果列表的中间项就是要找的项,那么此时查找成功。否则,我们至少可以将查找限定在 列表的前半部或者后半部,具体要依赖于所查找的目标值是小于还是大于我们的中间项。(记住, 前提是所查找的列表是已经排好序的。) 5.5 递归结构 155 屬__ :; T ♦的舞知锋聲 T 用:;充赛十系東方形辱本上象未荷:耸 :哼氣 疏聲^ , Mdtidria^, 1^2^X944) 「:_却麵萬3他感^々个长瀹癤:扇錄#,方形彝縴划耷咸暴小:: ―苌未 形参 :錄蟲 V ##以感翁成&索:琛逡一樹呈•画喁与束法类噼的_嫌,从申璋[坤程癉用: 封祢掩枚我 .嗖 个;^考艰上异婊 ? K •果炻藥省 :蜂过 襄表爭㈣龛条秦否是一## :: 谷 5.i 爷秦义 ^承法 g 養么你 的妨_者杈据:的、 . ■实际上^槔袭^个非确定性算 绛的; 例乎 ,因: : 为有祗 表地方 秦爽释 锋迷程妁人碑 —II 束作出“创造娃,’ 的决定 \也详邋就是為什么蒙德 | 黑安的砵杲褲&為為 t 本,而義釘的却不是。 ) ' : / = procedure f^onclFjan^ectangle jl if 《 拜你 的弟朵眼字減方 :f … : : ' ,— .•.i :I.H :: :, ., _.i |,., .. ,•: --i-r:^ ', r' -, '• -i- • -, ,0r S' -.-r ;' j 1 r ';^, ?:■•■> :n ';') CJ . ' '.• - .-:•-*.«.••'^ *.i i>'..j I. •, :|':, , •, .....I.; ,-r-, •;•'. -J : ,- T ^ r ,V.M ,,', ,'; - ; 'r;' ' ' 丫 丫 !+ . |-- ::滅遂甩韩寸个滅小.的捭方形 〆 ■: 把啦 ndriart 进程应用癒為一个较小的长方形 , ) 1 F ::::;:::獅巍奉_#_夢德索:癖紅赛许多龠暴索雜雜绛:申嘁 、 -样, [ ^序 ::猶溱論:考為^愁_雖鐵舞的無溱減择癖并 (: 爽難 2舍:)、 :选择 #溱'(私 -4 苹珂 妨‘ 納 :機 台錄方法) ^堆_序4鍊廉二 ft 巧 ■' 的拉表 , 可以# 尹 i 表笨輕 到摩禱 齊前_^参聲‘这 些算法 一轉 1 资祕說赛_本葦束轉 M 果舟_#'里面树4舲书 v sv / r :. V ”:' 在列表的剩余部分查找,我们可以使用顺序搜索法,但这里仍然使用在完整列表中所使用 的方法在表的剩余部分进行查找。也就是说,我们选择列表的剩余部分的中间项作为下一个要 考虑的项。像刚才那^¥,如果这个项就是我们要找的,那么查找结束,否则将把查找的范围限 定在更小的区域中。_ 图 5-12 简要概括了这个查找方法。这里我们要查找的是图左边列表中的 John 表项。首先 考虑中间项 Harry 。 可以看出,所查找的目标属于后半部分,接下来的查找将在原始表的后半部 分开始。该子表的中间项是 Larry , 显然所查找的目标在 Larry 之前,所以我们的注意力将转到 当前子表的前半部分。查看第二个子表的中间项时,我们找到了目标表项 John , 此时查找成功。 简言之,我们的策略是将被讨论的列表连续地分成更小的段,直到最终找到目标或者发现查找 被限制在一个空段中。 这里需要强调最后一点。如果所查找的目标值不在原始表中,那么我们的查找方法会将列 表不断分成更小的段直到所考虑的段为空,此时算法认为查找失败。 图 5-13 就是整个算法的一个伪代码草稿。这个草稿引导我们通过测试表是否为空来开始查找 过程。如果表为空,我们会被告知查找失败。否则,我们被告知要考虑中 间项。 如果此项不是目 标值,我们就被告知要查找表的前半部分或后半部分。这两种可能都需要第二次查找。显然,如 156 第 5 章算 法 果通过调用一个抽象工具的服务来执行这种查找将十分方便。尤其是,当我们应用一个称作 Search 的过程来执行第二次查找时。因此,为了完成我们的程序,就必须提供这样一个过程。 初始到表 第一个子表第二个子表 Alice B 曲 Carol David Elaine Fred George Harry trene _ John Kefly Larry Mary Nancy Oliver Irene John 編 1〆 Larry Mary Nancy Oliver Irene John Kelly 图 5-12 使用我们的策略在列表中查找 John 表项 If ( 表为空) then ( 报告查找失败) else [选择 List 的中间项作为 TestEntry ; 执行以下与条件相符的 case 指令块 case 1: TargetValue = TestEntry ( 报告查找成功) case 2: TargetValue TestEntry ( 在 List 中位于 TestEntry 项之后的部分查找 TargetValue , 并报告查找结果) lend if 图 5-13 二分搜索技术的草稿 但是这个过程应该执行相同的任务,而这个任务已经由前面所给出的伪代码表达。第一步, 我们将检查给定表是否为空,如果非空,它将开始考虑此表的中间项。因此我们可以提供一个 过程,只需把当前例程视为 Search 过程,并在二次查找的地方插入对这个过程的另外一次引用。 结果如图 5-14 所示。 注意,这个过程包含了一个对自身的引用。当然,如果遵循这个过程,然后到达指令 应用 Search 过程 . 我们将把同样的过程应用到一个较小的表上,而这正是应用于原始列表的那一过程。如果首次 查找成功,我们将返回并声明初始查找 成功; 如果第二次查找失败,我们将声明初始查找失败。 为了理解图 5-14 中的过程是如何运行的,我们假定一个表包括 Alice 、 Bill 、 Carol 、 David 、 Evelyn 、 Fred 和 George , 查找的目标值是 Bill 。 首先选择 David (中间项)作为考虑的测试项。因为自标值 ( Bill ) 小于该测试项,我们将对 David 之前的列表项调用 Search 过程(此时该列表是 Alice、BUI 5.5 递归结构 157 和 Carol )。 这样我们便创建了 Search 过程的第二个副本,并将它用于第二次查找任务。 Procedure Search ( List, TargetValue ) if ( 表为空) then ( 报告查找失败) else [ 选择 List 的中间项作为 Test Entry; 执行以下与条件相符的 case 指令块 case 1: TargetValue = TestEntry ( 报告查找成功) case 2: TargetValue TestEntry .( 应用 Search 过程查看 TargetValue 是否在 List 中位于 TestEntry 项之后的部分,并报告查找结果) lend if 图 5-14 二分搜索算法的伪代码 现在我们拥有两个正在执行的查找过程,如图 5-15 所示。先前的原始副本在执行 应用 Search 过程查看 TargetValue 是否在 List 中位于 TestEntry 项之前的部分 指令时被暂时挂起,此时我们应用笫二个副本完成对列表 Alice 、 Bill 和 Carol 的查找任务。完 成第二次查找后,我们将放弃该过程的第二个副本,并将它找到的结果通知原始副本,然后继 续原始的过程。通过这种方式,过程的第二个副本被当作原始过程的子过程运行,完成由原始 的模块请求的任务,然后消失 .pfrt Search AU $ t , eitVdtiell ' I Ust 'w, T ■ : ! 岛抑和 f 啡相 ㈣ 。 ㈤ UrtK 站拉 ’ 广 」?: 〆㈣ 七巧 ■ 明伸 hj— 如 ㈣ 拆 J H h 1^5 H , fAplpjy ^ p£Ci^infrr^^rcri 丁尹甲 妇制:麻: |L :::: 」 r : 和啤畔 部碑,叫吟 f:t 峰 Xn' r :- '■ j . cbi_ ■ 衆 ' - J-;-^,, r 'V; : .iVlrt We 诱 ^fb]ji TeitEntrv > 1 ' (Apply S^rch iib is iiriihe'pdrtwri of L|$t foMo'y/mg Testentryi and report the result of that saarchj 1 end If 我们在这儿 初 u.:;: procedure Search (Ust, Tar^etvalu^ ■, if (Ust empty)', . . (Neil Report that the eKe : [Sftlea ,! midd h* eniirv , i^i Llsc : to f .h «: ^&>Te^tE7iti:yf :& :,the ^I'Qc^rof^l hskuctfejtis' tfrat^is i ; : associated tfip 3ppr6p^iate tease, r ciie M Targeti\fatjJt ^TestEri^ty ■ ,, £R.€pjirt^that sQcc^^ded r > ' T^ttn^ry ' r . , {Apply the procedure S^ch ip 1 f H Tdjrg«Valtn 士 5 fn poftitm d List Wiov^ing and'report XHe result of that s^ch*) lwilff ! et-Vaiue : : featEntrvi List i Eve jy 6 st 择 ntry} Fred , i George \ 图 5-16 过程的第二个副本选择 Carol 当作当前项,同时推断目标必定存在于该表的后半部分。然 后,它将请求过程的第三个副本来寻找 Alice 和 Carol 组成的表中 Carol 后面的名字组成的表。 该子表是空表,所以过程的第三个副本需要在一个空表中寻找目标项。图 5-17 显示了目前所处 的情况。过程的原始副本处理表 Alice 、 Carol 、 Evelyn , Fred 和 George 的查找任务,测试项是 Evelyn ; 第二个副本处理表 Alice 和 Carol 的查找,测试项是 Carol ; 第三个副本将要在一个空 表中开始查找。 当然,过程的第三个副本很快就会推断它的搜索己经失败并终止运行。第三个副本的任务 的完成使得第二个副本能够继续执行。第二个副本发现它请求的查找失败,于是宣布自己的查 找失败并且终止。原始副本等到了第二个副本的报告,因此继续 执行。 过程的原始副本得到这 个消息后推断自己的过程失败并终止。我们的例程正确地推断 David 不在 Alice 、 Carol 、 Evelyn 、 Fred 和 George 组成的表中。 综上所述,如果回顾前面的例子,我们能够看到图 5-14 所示的算法重复 J : 也将所考虑的列表 分成两个较小的块,并将后续的查找严格限制在其中一个块中。这种一分为二的方法就是该算 法称为二分搜索 (binary search ) 的原因。 5.5 递归结构 159 p roc|i d ure : S earc h (L i st ^. ^Targfi.t jj ^) If CLisL ^jnpty) then (R€pon tiiatthe strarch failed.} - else [Select ihk "mMd>B* c eritfy m List tg be thtT^st^ntry; ■ the : b| o (jk : of ip^trtictio ns b^:ldw : that : !$■;::.:: assbciated witiiih 旮 a^ipropdaie c 也 se,!;. case.M TargatWalue ^Tlssttfitry , '(Report that the ^e^rch sticceeded.) case 2: TargetValgfe ^ TestEntry (Apply the procedure Search to see if TargerValue fs In the portion of the List preceding Test Entry, and report the result of that search.) cRse Tar-getValue > pstfinuy : 、 :(Apjply!i:he. 、 p.rQeeduFt Sear 中 : :to : :s.ee'l£; ; iT^fg^Vatuu L is In the' portt^n of Usi fofiowmg TestEmryr and repon the fesult of Ihatiearcti.) 1 end it pr4Ctitlur« Search (Usu Target vaJtie)' -if {List emp.ty) . (Ken export .that th« search,faf led o «lsc [Sdfittthfi k mtddie__ entry in \4S\ to the T^stEntry^ -ExecLitfr th^ blocker inSVUctiQ^ belo^V that ts 1 asSQCiatfii) with tihe appropti^t€ cast, case 1 i'Ta^etV^lue^ Te5ta>try .^fteport that the'Seaf;cti .succeetied^) case Z: TargetWlue ^ T«5tBit,ry ' {^ppiy the prficecfurfl Search to s,^ If Is Iruhti po^on 'of th* List pracedfrtg TesrErnry, arrd refl.oh tKe result of that search.) .case ,3- T^fltVglue > TeEtHhtrv ' ■: .{Apply the procedure Search to see if Targets I ue is in the portion of LIsi following TestEntry, and report the that'search^ J end If 我们在这 A PildCtidM^ev^earchiLiist IrargetV^e );.: . If Ujstemtit/) then ^portiliartli^ se^rcK failed.), : ;^:; : : ! ;:':::! i .;; ':.': 1 :.:, .::.;■ i .;;::;; i:r : i : : i ;.,;. xi : :,:■: ■ [Select tfe 如 fe 11 entry in List to .bje ttie T4&t£ntry; .the H bta h ck of rnf r StrUaiDhs b^low that is Hassoclat^d wkh the 1 appro pYlat^ case^ case h 'Jar^etVafue ^ TeslEmry - - Yf^iJortthatAbeisearctmieceededn). cas« 2: iafgetviJtie ^TestEntry .:::(^p ply^th^p roce^iiiJ F&§^arChji , is W pdrtidn of the us w . r ,.—._ T TI '- urw i: : W : se^ I f ftriiestVa) Lie and report tfie resuk ofthat'sea^) ■y.^ case ; 3 : T^fdetV^ue > ^PestEnirv ' , iAp^ly pro^d,u,r^ Sear , : , : k J 圓議 ,111 圓 Pp ___ 議 1^.. , - _ , ■- 、--二 ,- -- _ ' ‘ - ’ ■■ ■ -: ’ . 一‘ . _ ---• ... .:••• - - ,•- .•••• . .■ •. ‘ - • . • • 」 • - •-:-•- - •------- ... ; procedure Exertise ( N ) • •••. .•: •:' . :'.5. " . : .• :- -XX . --:-• :••- •:. -- : : .••• - - ... •••• ••:• •• ■-.•••• • -: -:•• if(N (« 2 )算法在输入大小增加的时候,对于时间将会有相近的需求 变化。此外,一个 ©( lg «) 算法就不会像0(« 2 )算法那样随着输入大小的增加对时间的需求扩张得如 此剧烈。 5.6.2 软件验证 回想波利亚对于问题求解的分析 (5.3 节),其中第4阶段就是对问题解决方案的准确性和其 作为求解其他问题的工具的潜力进行评价。这个阶段第一部分的重要性由下面的例子体现 出来: 一位拿着由7个金环组成的链子的旅行者必须在一个饭店里住7夜。每一夜的租 金是金链中的一环。应该怎样对链子进行最少次数的切割,旅行者才能每天早上支付 旅店的一环而不用提前支付住宿费? 首先我们认识到并不是每一环都必须被切开。如果只切开第二个环,那么我们就可以让第 一个环和第二个环与另外5个环分幵。按照这个想法,我们得到这样一种解,就是只需要切割 链中的第二、第四和第六个环,这个过程将所有的环分开而只对3个环进行了切割(如图 5-21 所示)。此外,任何更少次数的切割都会留下两个仍然连在一起的环,所以我们推断这个问题的 正确答案应该是3次。 进一步考虑这个问题,在只有第三个环被切开的时候,我们获得了 3部分金链,长度分别 是1、2和4 (如图 5-22 所示)。对这些块,我们可以进行如下操作。 第一天 早上: 给饭店一个环。 第二天 早上: 给饭店一个两个环的金链,同时找回一个环。 第三天 早上: 给饭店一个环。 第四天早上:把4个环的金链给饭店,同时找回原先给饭店的那3个环。 第五天 早上: 给饭店一个环。 第六天 早上: 给饭店一个两个环的金链,同时找回一个环。 第七天 早上: 给饭店一个环。 结果,第一个答案,也就是那个我们确定是正确的方法,实际上是错误的。但是,我们又如何 认定新方法是正确的呢?可能这样 反驳: 因为一个环必须在第一天早上给饭店,所以至少要从 金链上切一个环下来,同时因为新办法只需要一次切割,所以必定是最优的。 转换到程序设计环境中,这个例子强调了一个被认为正确的程序和一个正确的程序之间的 区别,二者并不一定相同。数据处理领域充满了可怕的事情,比如尽管“知道” 一个软件是正 确的,但最终还是因为一些没有预料到的情况而在关键的时刻发生错误。因此软件验证很重要, 并且发现有效的验证技术也成为了计算机科学中一个活跃的研究领域。 164 第 5 章算 法 割开 图 5-21 用3次切割将链子分开 割开 图 5-22 只用1步将链子分开 在这个领域内,研究的一条主线尝试把形式逻辑技术用于证明一个程序的正确性。也就是 说,目标是用形式逻辑来证明程序表达的算法确实做了它试图做的工作。基本的课题是通过将 验证过程化为一个形式化过程,防止那些可能与直觉有关的不准确的推断,就像金链问题一样。 让我们更详细地讨论如何把这个方法应用于程序验证中。 要感问 :这涉 爰电路蠢评泰机器刼造 的融扭 。七且,:质*.的杨趣孤在 软件中所做的那样,这意味着任何一个_微的错误都可能冉现在最终的产為中。一个例手是 20 世纪 40 年代由哈佛大学_造的马克一号计算机,它包含:了很多布线错谈雨这些错误狼多 -••• • . . —---—J: . --T - •- - , . ■ 』 . - , ■ • . "■ .. . • •- .•- •• 年都没有稜发现。一个_ 的例子 是在畢微处理華净浮点部分出_ 错误。 在这商个 例子中 ,镨误 都是在产生严重后果之前被 k 现的。 就好像形式数学证明基于公理(几何证明通常基于欧几里得定理,然而其他证明可能基于 集合论的公理),一个程序正确性的形式证明基于设计程序所使用的规格说明。为了证明一个程 序可正确地为一个姓名列表进行排序,不妨假设程序的输入是一个姓名列表,如果一个程序是 设计用来计算一个或者更多正数的平均值,则假设实际上输入由一个或多个正数组成。简言之, 正确性证明是从对于确定条件的假设开始的,这个条件称作前 提条件 ( preomditicm ), 以此来满 足程序执行开始的需要。 正确性证明的下一步是考虑这些预设条件的结果是如何在程序中传播的。为了这个目的, 研究人员分析了各种各样的程序结构来确定一个语句(一个在结构执行前被认为是真的语句) 是如何受到结构执行的影响的。作为一个简单的例子,如果在指令 X — Y 之前, 一 个关于 Y 值的 确定语句就已经得到,那么同样的关于 X 的语句就可以在指令执行以后获得确认。更准确地讲, 如果在指令执行之前己知 Y 的值不为0,那么也可以推断,指令执行以后 X 也一定不为0。 一 个稍微复杂的例子发生在下面这样的 if-then-else 结构中: if (条 件) then (指令 A) else (指令 B) 此处,如果某个己知的语句在结构执行以前已经获得,那么在执行指令 A 之前,我们立即知道 那个语句以及测试条件皆为真,相反如果指令 B 将被执行,我们知道语句和测试条件一定为假。 依照这些规则,可以通过识别语句,也就是断言 ( assertion ), 来进行正确性证明,断言能 够在程序的不同点建立。所得到的结果是一个断言集合,每一项都是程序预设条件的一个结果 以及可以导致在程序中某点建立断言的一组指令。如果在程序结尾建立的断言可以得到相应的 5.6 有效性和正确性 165 输出[称为后继条件 ( postcondition )], 我们就能够断定程序是正确的。 作为一个例子,考虑图 5-23 中所示的一个典型 while 循环结构。假设,作为在 A 点的已知 前提条件的一个结果,循环过程中每次终止测试的时候 ( B 点),我们都能确认一个特定断言为 真。对于一个存在于某个循环内部的某个断言,如果在每次执行到这个循环的这一点时均为真, 则称 作循环不变式 (loop invariant )□ 然后,如果循环一旦终止, C 点就会开始执行。此处我们 可以推断循环不变式和终止条件此时均成立。(循环不变式仍旧成立是因为终止测试不改变程序 中的任何值,终止条件成立是因为循环到此已经结束。)如果这此组合语句暗示着期望的后继条 件,我们的正确性证明仅仅通过初始化和修改最终导致终止条件的循环组件就可以完成。 前提条件 初 始化二 循环不变式 真 r 痼 坏体」 :簡' 循环不变式与终止条件 图 5-23 与典型 while 结构相关联的断言 应该拿这个分析与图 5-11 中关于插入排序的例子相比较。那个程序的外层循环基于下面的 循环不 变式: 每次终止条件测试执行的时候,从位置1到位置 iV -1 之间的项都完成了排序 并且终止条件是 的值大于列表的长度。 因此,如果循环终止,我们知道两个条件均已满足,这暗示整个列表已经排序。 程序验证技术发展的进步依然非常具有挑战性。即便这样,还是取得了一些进展,其中一 个更具重要性的进展是在编程语言 SPARK 中发现的, SPARK 语言与更为流行的 Ada 语言之间有 着紧密的联系。(关于 Ada 语言,我们将在下一章举例说明。)除了允许程序用像伪代码这样的 高层形式表示之外, SPARK 还提供给程序员包含判断的方法(如程序里的前提条件、后继条件 和循环不变式)。这样,用 SPARK 语言编写的程序不仅仅包含了应用的算法,还包含了形式化 正确性证明技术应用所需的信息。迄今为止, SPARK 已经成功地应用于涉及关键软件应用的很 多软件开发项目中,包括美国国家安全局的安全软件、美国洛克希德马丁公司的 C 130 J 大力神运 输机的内部控制软件以及关键铁路运输控制系统。 尽管 SPARK 成功了,但形式化程序验证技术并没有得到广泛的应用,因此今天大多数的软件通 过测试流程来进行“验证”,这个流程也还是不可靠的。毕竟通过测试进行的验证仅能说明程序对 166 第 5 章算 法 于测试的案例是正确的,而且任何附加的结论都仅仅是推测,程序中所包含的错误往往都是测试过 程中和程序开发中一些没有注意到的细微疏忽的结果。因此就像我们在黄金链中的问题一样,即使 做了很大的努力去避免它,程序中的错误往往还可能不被发现。 AT&T 发生过一个戏剧性的例子, 控制114交换站的软件中有一个错误,从1989年12月安装起到1990年1月15日都未被发现,在这段时 间里,一组独特的环境致使大约500万次呼叫被不必要地阻塞。 问题与-习 1. 假设有一台使用插入排廢算法编程的 机器, 排序 100 个名字的列表平均需要 1 s , 估算一下对 10 加个名 字的表排序需要多长时雨?对 10 000 个名 # 的表排序呢? 2■为下面的每种类型列出,个算法的例子; 敬 1辟)、#⑽和 f 将下鄕类型按有效性递織顺序排到: Ob 2 )® ®(lg 喊、 ©(«) 和痧(《 3 )。:: - 4, 考虑下面的问题和建议#案。看看建议的答案是 IE 确还是错误。为什么?- 问题;假设二个盒子里#3张卡片,其中一■张两面都涂成黑色,另一雜两面都涂成红色,第三张一面 涂成熏色,另一面涂成 C 色。抽出其中一张卡片,只允许看一面,那么另一面与你所看到的颜鱼相同 猜测#案:1/2。假设你看到的卡片的那一氣是红色的(如果是黑的,讨论结果也是一样的)。只有两 : 张卡片有红色的一面, B 此你看到的卡片必是这两张中的一张。这 g 中的一张背面德是红色彳另一 张背面就是黑色,_因此你看到的卡片背面是红色的概率和是黑色的_率一样大。 : 5. 下面的程序段用杂计算两个正整数(一个除数,^个被除数):的商 C 不考虑余数),方法是计算从被 除数中可以减去條数,葺到比除数小时减#次数。 la 如, 7 泼® 应 的禱果 应该为2,因为3可以从7中减 两次。这个程序査确吗?证明你的结论…〗 '. t , :: ._•! ; ;: , ,. SV : ;•、;:. .. . .: .. •:' - - I.:. : -: ; : i .'h- :: . >:• Count—O; -- Remainder^- Dividend; repeat ( Remainder Remainder - Divisor; until ( Remainder 6^. 下面的程序是通 过累计 X个 Y 的总和的方法来计算非负整数X和 Y 的_也就是说,3:乘以4就是计算3 • • •. • .. :| : •:.:;:.' ■ ::; ':•■ ;•:•;'::••': '; : ! . I :: ' ;: - :, . •;• •:. :. •• :' '• '::.V . ::; |1 ::. •"••; '; : ' ;;;:• r : 1; 个 4的意和。 T 酿段鍵对吗务雙觸你齡错论 。 r ,: ^ Product 一 Y; Count— 1; 〒 “ while (Count 2. 解释被提议的算法的歧义性和算法表示的歧 义性的区别。 3. 描述如何使用原语来帮助消除算法表示中的 歧义性。 4. 选择一个你比较熟悉的学科,并设计一种伪代 码来描述该学科。其中,要描述你要使用的原 语以及用于表示它们的语法。(如果想不出一 个科目,可以考虑体育、艺术或者工艺等。) 5. 下面的程序从严格意义上讲表示一个算法 吗?为什么? Count — 0; while ( Count not 5 ) do ( Count — Count +2) 6. 从什么意义上讲,下列 3 个步骤并不构成一个 算法? 第1 步: 在直角坐标系中从点(2,5)到点(6,11) 之间画一条直线。 第2步;在直角坐标系中从点(1,3)到(3,6)之间 画一条直线。 第3 步: 以上面两条线的交点为中心,画一个 半径为2的圆。 7. 用 repeat 结构代替 while 结构重写下面的 程序段,确保它能够输出与原程序相同的值。 Count 一 2; while ( Count 0 ) then ( 打印 N 的值并将 MysteryPrint 过 程应用于 N-2) 打印 N + 1 的值 35. 将下面的过程 MysteryPrint 的输入值设为2, 记录打印的值 D procedure MysteryPrint (N) if ( N>0 ) then (打印 N 的值并将 MysteryPrint 过程应用于 N-2) else (打印 N 的值并且 if ( N>-1 ) then (将 MysteryPrint 过程应用于 N + l )) 36. 设计一个算法,来(按递增顺序)生成其素数 因子为2和3的正整数的序列。也就是说,你的 程序应该产生这样的序列: 2, 3, 4, 6, 8, 9, 12, 16, 18, 24 , 27, …。 在严格意义上讲,你的程序 表示一个算法吗? 37. 按照列表 Alice 、 Byron 、 Carol 、 Duane 、 Elaine 、 Floyd 、 Gene 、 Henry 和 Iris , 回答下列问题。 a . 哪种搜索算法(二分法或顺序法)查找 Gene 更快? b . 哪种搜索算法(二分法或顺序法)查找 Alice 更快? c _ 哪种搜索算法(二分法或顺序法)能够比 较快地检测出名字 Bruce 不存在? d . 哪种搜索算法(二分法或顺序法)能够比 较快地检测出名字 Sue 不存在? e . 如果用顺序搜索方法查找 Elaine , 会进行多 少次比较?如果用二分搜索呢? 38. 0的阶乘定义为1。正整数的阶乘定义为整数本 身和比自己小的非负整数的阶乘。我们用记号 «!来表示整数 w 的阶乘,也就是说3的阶乘(写 作 3!) 是3乂(2!)=3乂(2\(1!))=3\(2\(1\ (0!)))=3 X (2 X ( lXl ))=6 o 请设计一个递归算 法来计算任意整数的阶乘。 39. a . 假设必须给一个有5个名字的列表排序,而且 已经有一个算法能给含4个名字的列表排序。 请利用已经设计好的算法来设计一个能给有 5个名字的列表排序的算法。 b . 基于问题 a 中使用的技术,设计一个能给任 意长的名字列表排序的递归算法。 40. 称为汉诺塔的难题有3根柱子,每个柱子都可 以放置若干个大小不同的环,这些环自底向上 直径越来越小。这个问题是,如何将一个柱子 上排列好的环移到另一个柱子上,规则是每次 只能移动一个环,较大的环不能放在较小的环 上面。我们看到,如果总共就只有一个环,那 么问题就非常容易。其次,当要移若干个环的 时候,如果你把除了最大的环之外的所有环都 搬到另一个柱子上,那么就可以把这个最大的 环搬到第三根柱子上,然后把其余的环搬到它 上面。利用这个分析,幵发一个递归算法来解 决任意环数的汉诺塔问题。 41. 解决汉诺塔问题的另外一个方法是把3根柱子 想象成一个圆圈排列,每根柱子在4点钟、8点 钟、12点钟的位置上^开始时,一根柱子上的 环从小到大以 I , 2 , 3等依次编号。最小的环编号 为1。看一根柱子上面的环,如果它的编号是奇 数,允许它按照顺时针方向移到下一根柱 子上; 如果它的编号是偶数,则允许它按照逆时针方 向移到下一根柱子上(只要不把较大的环放在 较小的环的上面)。在这个限制条件下,当几个 柱子上有可搬的环时,总是搬编号最大的环。 按照这个思路,开发一个非递归算法来解决汉 诺塔问题。 3 42. 开发两个算法,用来打印一个工人30天期间的 日薪,要求一个算法基于循环结构,另一个基 170 第 5 章算 法 于递归结构。这个工人每天的工资是前一天的 两倍(第一天的工资设为1便士)。如果在计算 机上实现你的算法,那么在数的存储上面会遇 到什么问题? 43. 开发一个算法来求一个正数的平方根。开始 时,把这个正数本身作为根的第一个猜测值, 以后按下列方法重复地产生新的猜 测值: 原正 数除以现在的猜测值得到商,取这个商和该猜 测值的平均值作为下一个猜测值。分析对这个 重复过程的控制,特别是,重复的终止条件是 什么? 44. 设计一个算法,列出一个由5个不同字符组成 的字符串中的字符的其他可能的排列。 45. 设计一个算法,在给定的名字列表中找到最长 的名字。如果列表里有多个“最长”的名字, 那么算法应如何解决?特别是,如果列表里的 所有名字的长度都一样,算法又应如何解决? 46. 设计一个算法,对于一个有5个或更多表项 (数)的列表,在不对整个列表进行排序的情 况下,找出5个最小的和5个最大的表项。 47. 对名字 Brenda 、 Doris 、 Raymond 、 Steve 、 Timothy 和 William 进行排序,要求在使用插入 排序算法(图 5-1 1 ) 进行排序时比较次数最少。 48. 对于有4000个名字的列表,使用二分搜索算法 (图 5-14) 时最多检查多少个表项?使用顺序 搜索算法(图 5-6) 呢?试对二者进行比较。 49. 使用大0记号对传统的小学加法和乘法的算 法进行分类。也就是说,如果两个有《个数字 的数相加,那么要做多少次一位的加法?如果 两个有《个数字的数相乘,那么要做多少次一 位的乘法? 50. 有时对一个问题稍作变动就可能使它的解的 形式发生重大改变。例如,设计一个简单的算 法来解决下述问题,并用大0记号进行 归类: 把一群人分为两个小组(人数不限),使得两 个小组成员的年龄的总和的差尽可能大。 现在把问题改为,使得两个小组成员的年龄的 总和的差尽可能小,再利用大©记号进行归类。 51. 从下面的列表中找出一组数,使其总和等于 3165。你的解法效率如何? 26, 39, 104, 195, 403, 504, 793, 995, 1156, 1677 52. 下面例程中的循环会终止吗?解释你的回答。 如果这个例程实际在一台计算机上执行(见 1.7 节),说明可能会发生的情况。 X — 1; Y —1/2; while ( X 不等于 0 > do (X— X - Y; Y-Y+2 > 53. 下 面的程序段用来计算两个非负整数 X 和 Y 的 乘积,方法是累计 X 个 Y 的和。也就是说,3乘 以4是通过累计3个4得到。这个程序段正确 吗?为什么? Product— 0; Count — 0; repeat(Product— Product + Y, Count— Count +1) until(Count = X) 54. 下面的程序段是用来报告正整数 X 和 Y 中哪个 大的,这段程序正确吗?为什么? Difference X-Y; if ( Difference 是正数) then (print "X is bigger than Y ”) else (print “Y is bigger than X ”) 55. 下列的程序段用来从一个非空的整数列表中找 到最大的项。这个程序段正确吗?为什么? TestValue — first list entry; CurrentEntry —first list entry; while ( CurrentEntry 不是最后一项 ) do (if ( CurrentEntry > TestValue ) then ( TestValue—CurrentEntry ) CurrentEntry— 下一个表项) 56. a . 标识图 5-6 表示的顺序搜索算法的前提条 件。为这个程序里的 while 结构确定一个循 环不变式,当它与终止条件结合时,就意 味着,在该循环终止时该算法将正确地报 告成功或失败。 b _ 给出一个论据说明图 5-6 里的 while 循环事 实上是会终止的。 57. 基于赋给 X 和 Y 的值是非负整数的前提条件, 标识下述的 while 结构里的循环不变式,当它 与终止条件结合时,就意味着,与 Z 相联系的 值在循环终止时一定是 X - Y 。 Z—X; 0; while (JiHUW«w»)wi. ,.,l -N h , t ' M H >■ I J - L ’ 丄 I 卜卜, H ‘ _ 1 . " 通常,程序由一组语句组成,这些语句一般可以分成3 类: 声明语句、命令语句和注释 。声 明语句 (declarative statement ) 定义了在程序中使用的需要自定义的术语,如用来引用数据项的 名称; 命令语句 (imperative statement ) 描述了潜在的算法里的 步骤; 注释 ( comment ) 则通过 比较人性化的形式来解释程序中的一些复杂特性,从而提高了程序的可读性。通常,命令语言的 程序(或者面向对象程序中的命令型语言程序单元)可以被认为具有图6~4描述的结构。它以描述 程序所操作的数据的一组声明语句开始,紧接其后的是描述被执行的算法的命令语句(图6~4)。现 在,很多语言都允许声明语句和命令语句自由交织存在,但其概念上的区别依然存在。注释语句是 很分散的,仅仅出现在需要对程序进行解释的地方。 程序 _第一部分由声明语句组成, _描述该程序要操作的数据 第二部分由命令语句组成, 描述该程序所要实现的动作 图 6-4 —个典型的命令型程序或程序单元的结构 180 第 6 章程序设计语言 根据指引,我们通过语句目录来进行编程概念的研究,该语句目录的顺序是我们在一个程 序中可能遇到的这些语句的顺序,以与声明语句有关的概念开始。 6.2.1 变量和数据类型 正如在 6.1 节中提到的那样,高级程序设计语言允许使用描述性的名字指代存储器地址, 而不必再使用数字地址,这样的名字称为变量 ( variable )。 之所以这样取名是因为,随着程序 的执行,只要改变了存放在这个存储单元里的值,那么与该名字相联系的值就改变了。在程序 使用这个变量之前,我们的示例语言要求必须通过一个声明语句建立变量。同时,声明语句也 会要求程序员描述变量所指代的将存储在存储器地址中的数据的 类型。 这样的类型称为数 据类型 ( datatype ), 它决定了数据的编码方式以及在该数据上可执行的 操作。例如, 整型 就是可能以二进制补码形式存储的数值型数据,它是由全体整数组成的。可 以在整型数据上进行的操作包括传统的算术运算和比较运算,如判断一个数是否比另一个数大。 实型 ( real ) 有时也称为 浮点型 ( float ), 是指可能以浮点形式存储的整数之外的数值型数据。 可以在实型数上进行的操作很类似于那些可以在整型数上进行的操作,但是注意,把两个实型 数相加与把两个整型数相加是两个不一样的操作。 假设我们需要在 一 个程序中使用变量 WeightLimit 来指代主存中以二进制补码形式编码的 数据值的地址。在程序设计语言 C 、 C #、 Java 和 C # 中,我们可以在程序的头部插入声明 语句: int WeightLimit ; 这个语句的意 思是: “名字 WeightLimit 将要在后面的程序中用到,它指代一个以二进制补码 记数法表示的存放在存储器某个区域的值。”同一类型的多个变量通常在同一个声明语句中声 明。例如,语句 int Height, Width ; 声明了两个整型变量 Height 和 Width 。 此外,大多数语言允许在变量声明时,为变量赋一个初 始值。因此,语句 int WeightLimit = 100; 不仅声明了一个整型变量 WeightLimit , 而且还为这个变量赋了一个初始值100。 其他通用数据类型还包括字符型和布尔型。 字符型 ( character ) 指的是由符号组成的数据, 它们通常使用 ASCT 码或者 Unicode 字符集进行编码存储。可以在这种数据上进行的操作包括 比较运算,如按照字母顺序判断一个字符是否在另一个字符的 前面; 判断一个字符串是否是另 一个字符串的子串,以及将一个字符串连接在另一个字符串的尾部从而形成一个更长的字符串 等。语句 char Letter , Digit ; 在程序设计语言 C 、 C ++、 C # 和 Java 中用来声明两个字符型 变量: Letter 和 Digit 。 布尔型 ( Boolean ) 是仅仅有真和假两个值的数据类型。可以在布尔型数据上进行的操作包 括判断当前的值是真还是假。例如,如果变量 Limit Exceeded 被声明为一个布尔型变量,那 么下面这种形式的 语句: if (LimitExceeded) then (...) else (...) 是很合理的。 作为原语包括在程序设计语言里的数据类型(像对于整型的 int , 对于字符的 char ) 称为 基本数据类型 ( primitivedatatype )。 我们所知的整型、实型/浮点型、字符型、布尔型是通用的 原语,其他数据类型(包括图像、音频、视频以及超文本)目前还没有成为程序设计语言的通 6.2 传统的程序设计概念 181 用原语。但是,像 GIF 、 JPEG 和 HTML 这样的类型可能马上就要像整型和实型一样通用。在 6.5 节和 8.4 节,我们将学习到面向对象范型如何使程序员在一门编程语言提供的原始数据类型 基础之上扩展可用的数据类型。的确,这种能力是面向对象范型被称赞的特性。 下面程序段是用 C 语言及其派生语言 C ++、 C # 和 Java 表达的声明语句。变量 Length 和 Width 声明为实型/浮点型,变量 Price 、 Tax 和 Total 声明为整型,变量 Symbol 声明为字符型。 float Length, Width ; int Price, Tax, Total; char Symbol; 在 6.4 节,我们会看到翻译器如何利用从这些说明语句中收集到的知识,把一个程序从高 级语言形式翻译为机器语言形式。这里,我们要注意,这些信息可以用来识别错误。例如,对 于两个早先声明为布尔类型的变量,如果翻译器发现一个要求对它们做加法的语句,那它很可 能认为这个语句是错误的,并把这个结果报告给用户。 6.2.2 数据结构 除了数据类型,程序中的变量通常与数 据结构 (data structure ) 相联系,即数据在概念上的 形态与布局相联系。例如,文本通常被看做是一个长的字符串,而销售记录可能被看为数字值 的矩形表,其每一行代表了某位销售人员完成的销售,而每一列代表了某一天所完成的销售。 一个常用的数据结构是数组 ( array ), 即一块相同类型元素组成的数据块,如一维表、一个 由行和列组成的二维表或更高维数的表。为了在程序中建立这样的数组,大多数程序设计语言 要求声明语句在声明数组名字的同时也要明确指出数组每一维的长度。例如,图 6-5 显示了由 C 语言语句 int Scores [2] [9] ; 声明的概念上的结构,它的意思是变量 Scores 将要在后面的程序中使用到,并且是一个有2行 和9列的二维的整型数组,而在 FORTRAN 中同样的声明语句要写成 INTEGER Scores(2,9) 一旦声明了一个数组,就能够通过它的名字在程序中的任何地方引用它,或者通过一个称作索 引 ( index ) 的整数值来标识这些数组的组成元素,索引明确了行、列等所需的信息。但是,索 引的范围在不同的语言中是不同的。例如,在 C 、 C ++、 Java 和 C # 语言中,索引从0开始,也就 是说对 Scores 数组(上文已定义)的第2行第4列的项应该用 Scores [1] [3] 来引用,而第1行第 1列的项应该用 Scor es [0] [0] 来引用。相反,在 FORTRAN 程序中索引是从1开始的,所以第2 行第4列的项对应于 Scores [2] [4] (可再参考图6-5)。 Scores 在 FORTRAN 中写作 Scores ( 2 , 4 ) ,索 引从1开始 在 C 及其衍生的语言中写 作 Scores [1] [3] ,索 引从0开始 图 6-5 拥有2行9列的二维数组 182 第 6 章程序设计语言 相比由同一种数据类型的数据元素组成的数组, 聚合类型 [aggregate type , 也称结构 ( structure )> 记录 ( record ), 有时还称异 构数组 (heterogeneous array )] 是其元素可能具有不 同类型的数据块。例如,一个雇员的数据块也许包括一个字符型的 Name 、 一个整型的 Age 以 及一个实型的 SkillRating 等条目。这样的聚合类型用 C 语言声明如下: struct{char Name[25]; int Age; float SkillRating; } Employee; 上述声明意思是:变量 Employee 指向一个结构(即缩写的 struct ), 这个结构有3个构成 元素: Name (包含25个字符的字符串)、 Age 和 SkillRating (见图6-6)。一旦声明了一个这样 的集合体,程序开发人员就可以使用这个结构的名字 ( Employee ) 来指向整个集合体,或者用 结构的名字跟一个圆点和字段名 C 如 Employee . Age ) 来表示集合体中的单个字段 ( field )。 Meredith W Uiismeyer Emp loyee.Name Employee — Employee.Age Employee.Ski11Rating 图 6-6 异构数组 Employee 的概念结构 在第8章,我们将会看到诸如数组这样的概念结构是如何在计算机内部真正实现的。特别 是,我们将会学到, 一 个数组里面的数据可以散布在主存储器或海量存储器上的广大区域内,这 就是将数据结构表达成概念上的数据形态或者数据布局的原因。当然,计算机存储系统中的实际 布局也许会与概念上的布局有很大的不同。 6.2.3 常量和字面量 有时,在程序中要用到预先确定的固定值。例如,一个管理机场附近区域空中交通的程序, 也许要许多次引用一些关于机场的海拔高度的数据。当编写这样一个程序的时候,在每次需要 这个数据时,我们都可以以数字的形式将其引入(如 645 m )。 一个值的这样一种显式出现称为 字面量 ( literal )。 字面量的使用导致了诸如 EffectiveAlt 一 Altimeter + 645 这样的程序语句的出现,其中 EffectiveAlt 和 Altimeter 是假定的变量,而645是一个字面 量。这样,赋给变量 Altimeter 的值加上645的结果赋给了变量 EffectiveAlt 。 在大多数程序设计语言中,由文字组成的字面量用引号来表述,以便与其他程序部分相区 分。例如,语句 LastName — " Smith" 可以用来把文字 “ Smith ” 分配给变量 LastName , 而语句 LastName — Smith 6.2 传统的程序设计概念 183 则是把变量 Smith 的值赋给变量 LastName 。 通常,使用字面量不是一个好的编程习惯,因为字面量会掩盖包含字面量的语句的真实意 义。例如,当一个读者读到 EffectiveAlt 一 Altimeter + 645 这个语句的时候,他如何知道这个645代表的是什么呢?此外,字面量的使用会使在必要时修 改程序的工作变得复杂。如果将空中管制程序移植到另一个机场,那么所有对机场海拔高度的 引用都将要修改。如果每一处对海拔高度的引用都使用了字面量 645, 那么要在整个程序中定 位每一个这样的引用并且加以修改。再假设在数量上,而不仅仅是在海拔高度上,同时也使用 了这个字面量 645, 这个问题将会变得更加复杂。我们如何能够知道哪个 645 需要保留,哪个需 要修改呢? 为了解决这个问题,程序设计语言允许为特定的不会改变的值分配一个描述性的名字。这 个名字称为常量 ( constant )。 例如,在 C ++ 和 C # 语言中,声明语句 const int AirportAlt = 645; 将标识符 AirportAlt 与一个固定的值 645 (我们认为它是整型的)联系起来。在 Java 语言中, 类似的概念表达为 final int AirportAlt = 645; 根据这些声明,描述性的名字 AirportAlt 能够用于字面量 645 出现的场合。若将这种常量用于 伪代码中,语句 EffectiveAlt 一 Altimeter + 645 可以改写成 Eff ectiveAlt Altimeter + AirportAlt 这种方式能够较好地表达语句的含义。此外,如果用这样的常量来替代字面量,当程序要移植 到另一个海拔高度为267英尺的机场时,仅仅修改这个定义常量的声明语句就可以将对机场海 拔高度的所有引用改为新的值。 6.2.4 赋值语句 一旦声明了用于程序的专门术语(如变量和常数),程序员就可以描述涉及的算法了。这要 依靠命令语句。最基本的命令语句就是赋 值语句 (assignment statement ) ,它将一个值赋给一个 变量(更确切地说,存放在该变量所标识的存储区域中)。这样的语句的语法结构通常是由变量 和一个代表赋值运算的符号以及赋值表达式组成。这种语句的语义就是通过表达式求值得到结 果,从而把结果作为变量的值来存储。例如, C 、 C ++、 C # 和 Java 语言中的语句 Z = X + Y ; 是将 X 和 Y 相加的和赋给变量 Z 。 在一些其他语言(如 Ada ) 中,等价的语句可以写成 Z := X + Y ; 注意,这些语句仅仅在赋值运算符语法表示上不同,在 C 、 C 杆、 C # 和 Java 语言中,仅仅使用一 个等号,而在 Ada 中,要用一个冒号加等号的形式来表示。也许,一个更好的赋值操作符号是 APL 语言中所使用的, APL 是由 Kenneth E . Iverson 在1962年设计的。 ( APL 是 A Programming Language 的缩写。)它使用一个箭头来表示赋值。因此,前面的赋值在 APL 语言中可以表示为 184 第 6 章程序设计语言 (亦 与第5章的伪代码中的一样)。 赋值语句的许多功能都与语句右边的表达式的作用域关系密切。一般而言,任何一个代数表 达式都可以用在赋值表达式中,包括通常用+、-、 * 以及/符号分别代表的加、减、乘、除算术 运算。一些语言把**组合用来求幂。例如,在 Ada 中,表达式 表示 x 2 。 但是,各种语言对这种表达式的解释是不一样的。例如,对于表达式 2 M +6/2, 如果是从 右向左求值,可以得到一个值14,而从左向右求值将得到7这个结果。这种不确定性通常是通过 运算符优先级 ( operatorprecedence ) 规则来解决的,这意味着某些运算比其他运算优先。传统的 代数规则指定乘和除要在加与减之前执行。根据这个惯例,前面的表达式的结果应该是11。在大 多数语言中,括号比所有的运算符的优先级都高。因此,2*(4+6)/2的结果应该是10。 许多程序设计语言允许使用相同的符号表示多种类型的运算。在这些情况下,符号的意义 只能根据操作数的数据类型来决定。例如,当操作数是数值时,符号+传统上表示加法。但在某 些语言里,如 Java , 当操作数是字符串时,该符号表示连接。也就是说,表达式 "abra" 4 - "cadabra" 的结果是 abracadabra 。 一个运算符的这种多种用法称 为重载 ( overloading )。 许多程序设计语 言提供了一些常见运算符的内置重载,而另外一些程序设计语言(如 Ada 、 C ++ 和 C #) 可能允 许程序设计人员定义额外的重载的意义,甚至添加额外的运算符。 6.2.5 控制语句 控制语句 (control statement ) 是可以改变程序中语句执行次序的命令语句。在所有的程序 设计结构中,某些控制语句受到了极大的关注并且引发了很大的争议。主要起因是最简单的控 制语句—— got ◦语句。它提供了一种把执行顺序转向另一个位置的手段,这个位置是用名字或 数标记的,这仅仅是机器语言级的 JUMP 指令的直接应用。但在高级语言中,这个特点意味着程 序员将写出像 goto 40 20 Apply procedure Evade goto 70 40 if (KryptoniteLeve 1 statementA; WHEN 1 B' 二 > statements; WHEN 1 C' => statementC ; WHEN OTHERS => statementD ; END CASE 186 第 6 章程序设计语言 另外还有一个称为 for 结构的常见控制结构(如图 6-7 所示,这是 C ++、 C # 和 Java 语言中的表 示)。这个循环结构与伪代码中的 while 语句相似,不同之处在于循环的所有初始化、修改和终 止都在一个语句中进行。当循环体对于指定范围内的每个值都要执行一次时,这样的语句就很 方便。特别地,图 6-7 中的语句指示循环体被重复执行,第一次 Count 的值为1,第二次 Count 的值为2,第三次 Count 的值为3。 False -- -rf = 。鵠篇 奴: True : ’齡靡檀據 - 1 V - . _■ s " T ■ ■, for (int Count = 1; Count 本聲袭氮树木赛見森祙: ' ' : ' 根据所引用的例子,我们可以得到这样一个结论,即通用的分支结构存在于所有命令型程 序设计语言和面向对象程序设计语言中,而且仅有细微的变化。从计算机科学的理论中我们可 以了解到一个有些令人吃惊的结论,这就是仅仅需要这些结构中的一小部分就足以保证程序设 计语言解决所有可以由算法解决的问题。我们将会在第12章研究这个问题。现在,我们只是指 出,学习程序设计语言不是一个无休止的学习各种控制语句的过程,在当前的程序设计语言中 可以找到的大多数控制结构本质上都是这里介绍的这些结构的变体。 6.2 传统的程序设计概念 187 ’〈:. 立 .- 輪蠡涵兔舞遍•-••戀象_^_^義_^^'豫_^#継: 的⑽户哥戍魏姆 j 的爸01與 H 程萬。 ^ M : : Vi$M 義峰 它春; . 屬鱗趣雜:羅:: i ■动鱗__職 溱 〆 : 并域魏 1 轉•啕 ___^ w 轉崔_4' 例暴,—乎_而豪> _序员 - 以果义事击餐钮巧争发¥ 什桑事 雍::轉_我_将_学# 被广泛I用的:程序设计赛言:。如今微巍 公司 又开:发出了 C#, 逸种优静是萑能赞继凑我们 将編 S 待 。:: - 二 6.2.6 注释 不管一种程序设计语言设计得多么好,也不管一个程序把该语言的特性应用得多么出色, 当人们试图理解这个程序时,程序附加的信息通常能起帮助作用,或者是必须要有的。因此, 程序设计语言提供了在程序中插入解释性语句方法,这些语句就 是注释 ( comment ). 翻译器是 忽略注释语句的,因此从计算机的角度来说,注释的存在与否都不影响程序的执行。无论源程 序有没有注释,对于翻译器生成的程序机器语言版本是没有影响的,但从人的角度来看,这些 注释是程序的重要组成部分。若没有这些注释,对于大的复杂的程序,程序员对程序的理解肯 定会受到很大的影响。 在程序中加入注释的方法通常有两种。一种是用两个特殊的记号将整个注释括起来,一个 在注释的起始位置,一个在注释的尾部。另一种是标示出注释的起始位置,标记符号右边的字 符全部都属于注释。在 C #、 C # 和 Java 中,我们可以同时看到这两种注释方法的应用。它们用 记号 /* 和 V 把注释括起来,或者用记号//开始一个注释直至行末。因此, /* This is a comment. */ 和 // This is a comment - 都是合法的注释语句。 通常,注释要求用词少,而且含义明确。当为了制作内部文档而要求一些初级程序员使用 注释语句的时候,他们容易为 ApproachAngle = SlipAngle + HyperSpacelncline ; 这样的语句写出类似“把 SlipAngle 和 Hyper Space 工 nc line 相加得到 ApproachAngle 的值”这 样的注释。这样的冗余的话增加了程序的长度,但却没有解释程序。记住,注释的目的就是解释 程序,而不是重复。对于这条语句的一个更合适的注释应该是解释为什么要计算 ApproachAngle (如果这一点不很明显的话) □ 例如,注释 “ApproachAngle 将会在计算 ForceFieldJettison- Velocity 时使用,并且在此后就不再使用了”就比前面的注释更有用一些。 此外,分散在程序之中的注释有时会影响人们跟踪程序流程的能力,因此使理解程序变得 比没有注释的时候还要困难。一个好方法就是将关于某个单一程序单元的注释统一放在一个位 置上,比如放在该程序单元的开始位置。这就给读者提供了程序单元注释的确切地点,同时也 提供了可以用来描述此程序单元的目的和综合特性的地点。如果这个格式在所有的程序单元中 都采用了,写出来的程序就能在某种程度上达到一致性——每个程序单元都包括一组解释性的 188 第 6 章程序设计语言 致性提高了程序的可读性。 语句,以及随后对该程序单元的正式表示。程序中的这种 问题与练零 1. 为什么使用常量比使用_»的程序设评編格更好? ' :: ' ' 1;! : ' ;; :, . r :; : n 4. 给出命#型程序谤计语參相面向对嶔程序樣计语言里的一些通用控制结构。 5. 数组和曼抅数组之间的区細是什么? - • _ - • - , I- -7. 7 .- :: _■ i rX .- : - - : :-: =^ 22 .::. ::, - : - " T.r ';~r:t -- v -- - - • : --X :: 6.3 过程单元 在前面的章节中,我们已经看到了将大程序拆分成小的可管理的单元的一些好处。在本节 中,我们将主要讨论过程这个概念,过程是一个命令型语言获得程序的模块化描述的主要技术。 而且,在面向对象语言中,过程也是程序员指定对象如何响应外部激励的工具。 6.3.1 过程 从一般的意义上来说,过程 ( procedure ) 就是实现一个任务的一组指令的集合,它能够作 为其他程序单元使用的抽象工具。当请求了过程提供的服务时,程序的控制权就转移给了过程, 在过程执行完之后,程序控制权返回到最初的程序单元(图6-8)。将控制权转移给过程的步骤 经常称为调用 ( call 或者 invoke )。 我们将一个请求过程执行的程序单元称为调用单元。 调用程序单 元请求过程 调用程序单 元继续 调用程序单元 控制权传递到过程 过程 当过程完成时,控制 权返回到调用单元 过程被执行 图 6-8 包含一个过程的控制流 在第5章的伪代码中,过程通常以独立程序单元的形式来编写,单元以一个称为过程头 ( procedure’s header ) 的语句开始,它标识了(在其他事情中间)过程的名称。过程头后面是定 义过程细节的语句。这些语句往往以与传统的命令程序相同的方式排列,以声明语句开始(说 明了过程中使用的变量),接着是命令语句(这些命令语句描述了过程执行时要履行的步骤)。 一般来说,在过程中声明的变量称为局部变量 (local variable ), 意味着它只能在这个过程 的内部使用。如果两个独立的过程都使用同一个变量,这很可能产生一定的混乱,而局部变量 可以减少这样的冲突。 [一 个程序中可以引用某个变量的部分称为该变量的作用域 ( scope )。 因 此,局部变量的作用域就是声明它的过程。没有限制在程序中某个特定部分使用的变量称为全 局变量 (global variable ), 它们可以在程序的任何地方使用。大多数程序设计语言提供了声明局 部变量和全局变量的方法。] 6.3 过程单元 189 第 5 章中的伪代码使用了诸如“应用过程 DeactiveKrypton ” 这样的语句来请求过程的 执行,但现在大多数的程序设计语言允许只通过写出过程名来调用过程。例如,如果 GetNames 、 SortNames 和 WriteNames 都是过程的名字,它们的功能分别是获得名字的列表、将列表排序 以及打印这个列表,那么获取、排序及打印该列表的程序可以 写成: GetNames ; SortNames ; WriteNames ; 而不是: 应用过程 GetNames . 应用过程 SortNames - 应用过程 WriteNames . 注意,通过为每一个过程指定一个可以描述过程的功能的名字,这种简明扼要的形式看起来就 像是反映该程序含义的命令序列。 6.3.2 参数 过程通常会使用一些通用项,这些项只有在过程被执行的时候才可以确定下来。例如,第 5 章中的图 5-11 给出了一个列表排序过程的伪代码,其中的列表不是某个特定的列表,而是一 个通用的列表。在伪代码中,我们决定在过程头的括号中标识出这些通用项。因此,在图 5-11 中,过程以 procedure Sort(List) 开始,并在接下来使用 List 指代需要排序的列表,从而进一步描述列表排序过程。如果要应用这 个过程来为一个参加婚礼的客人列表排序,我们仅仅需要假设通用项 List 指代参加婚礼的客人列 表。如果将要排序一个会员列表,我们只要将通用项 List 解释成该会员列表。 过程内部的这些通用项称作参数 ( parameter )。 更准确地说,这些在过程内部使用的项称为 形参 ( formalparameter ), 并且当过程被调用的时候,赋给形参的值称为实参 ( actualparameter )。 在某种程度上,形参就像是过程体上的槽口,而当过程被调用的时候,实参就好似被塞入了这 个槽口中。 就伪代码来说,大多数编程语言要求在定义一个过程时将形参列在过程头的括号里。例如, 图 6-9 给出了用 C 语言编写的名字是 ProjectPopulation 的过程的定义。该过程期望在它被调 用的时候,接受一个确定的年增长率值。在这个增长率的基础上,假设初始数量为100,过程计 算出未来10年中某个种群的数量,并且将结果存储在称为 Population 的全局数组中。 大多数程序设计语言在调用过程的时候也使用括号来标识实参。也就是说,调用过程的语 句要包括过程的名字并在紧接名字的括号中给出实参的列表。因此,像 使用增长率 ◦ . ◦ 3应用过程 Pro j ectPopulation 这样的伪代码语句可以用 C 语言语句 ProjectPopulation (0. 03 ) ; 表示,它是用增长率的值 0.03 来调用图 6-9 中的过程 Project Population 。 当过程不只包含一个参数的时候,实参要与过程头的形参序列一一对应——第一个实参对 应第一个形参,依此类推。然后,实参的值就可以有效地传递给它们相对应的那个形参,从而 过程得以执行。 190 第 6 章程序设计语言 以 void 开始过程头是 C 语 言程序员指定程序单元是 过程而不是函数的方法。 我们很快就将学到函数 形参列表。注意,像大多 数程序设计语言一样 , C 语言要求对每个参数说明 数据类型 i-i|;1 1 void, Pro j ectPopulation ,:(f int ¥ear; 声明一个名为 Year 的局部变量 Population DO:] ^, 1U0, . 0 . ; - J、: 1 / ■■ Popu]la;tiofi [Year 单 1] - ■二 Pa'piiiatiorilY^arl V '. (^'o^u 1 at loti ; [ ; ,^ ;' in^^e)' ...:沿 H:_C 这些语句描述人口如何计算并存储在名 为 Population 的全局数组中 图 6-9 用 C 语言编写的过程 ProjectPopulation 为了强调这一点,假设过程 PrintCheck 使用这样的过程头来 定义: procedure PrintCheck(Payee, Amount) 其中 Payee 和 Amount 是过程的形参,它们分别指代了将支票支付给哪个人以及支票的数额。那 么,用语句 PrintCheck ("John Doe, 1 ,15 0) 调用过程,将会使得形参 Payee 与实参 John Doe 对应,形参 Amount 与实参15 Q 对应,从而过程 得以执行。但是使用语句 PrintCheck( 150, "John Doe") 调用过程将会使得值150赋给形参 Payee , 而 John Doe 赋给形参 Amount , 而这必定会导致错误 的结果。 对于形参和实参之间的数据传递,不同的程序设计语言有不同的处理方法。在某些语言中, 对于实参所表示的数据会产生一个副本并传给过程。使用这种方法,过程对数据的任何修改仅仅 是对副本的修改——调用程序单元中的数据并没有被修改,我们称之为按值传递 (pass by value )。 注意,按值传递参数可以保护调用单元中的数据,使之不会被设计有问题的过程错误地修改。例 如,如果调用单元传递一个雇员的名字给一个过程,我们当然希望过程不要改变这个名字。 但是,当参数表示一个很大的数据块时,按值传递参数效率不高。一个更高效地给过程传 递参数的方法,就是在调用程序单元中告诉过程它所需的实参的地址,从而使过程可以对实参 进行直接存取。对于这种方法,我们称为按引用传递 (pass by reference )。 注意,按引用传递参 数允许程序修改调用单元中的数据。这个方法对于为列表进行排序的过程来说是很有用的,因 为调用这样的过程目的就是改变列表。 例如,假设 一 个过程 Demo 定义为: Procedure Demo(Formal) Formal-*-Formal + 1; 此夕卜,假设变量 Actual 被赋予一个值5,我们用下面的语句调用 Demo : Demo(Actual) 6.3 过程单元 191 然后,如果参数是按值传递的,在过程中对 Formal 的改变不会影响变量 Actual 的值(参见图 6-10)。但是,如果是按引用传递的话,那么 Actual 的值将会增加1 (参见图6-11)。 ( a ) 当过程被调用时,数据的副本给该过程 调用环境 过程的环境 ... • ... : __ ■ ■■ 5 1. : .'r ■ ^ 6 ■- : . ■■ ,-: ■ , ( b ) 该过程操控数据的副本 调用环境 ..—'_z " 5 = ( c ) 于是,当过程终止时,调用环境没有改变 图 6-10 执行过程 Demo , 按值传递参数 调用环境 过程的环境 ( a ) 当过程被调用时,形参变成对实参的引用 调用环境 过程的环境 0>)于是,该过程所做的改变是针对实参的 调用环境 :实参义' ... ' . * "6 ( c ) 因此,在过程终止后过程所做的改动被保留下来了 图 6-11 执行过程 Demo , 按引用传递参数 192 第 6 章程序设计语言 _ 不同的程序设计语言提供了不同的参数传递技术,但是在任何情况下,参数的使用都允许 过程以通用的意义书写,并在适当的时候应用于特定的数据。 6.3.3 函数 让我们暂停一下来考虑过程概念的一个微小的变化,该变化存在于许多程序设计语言中。 有时,过程的目的是要产生一个值,而不是完成一个动作。(考虑这样两个过程之间的差别,一 个过程是估计售出的小商品的数量,另一个过程用来玩一个小游戏,前者重点是为了产生一个 值,而后者是为了完成一个动作。)如果目的是产生一个值,那么这个“过程”是作为一个函数 来执行的。这里,函数 ( function ) 是指一个类似于过程的程序单元,但它把一个值作为“该函 数的值”传递给调用程序单元。也就是说,函数的执行就是计算出一个值并且将这个值送回到 调用程序单元中。这个值可以存储在一个变量里为以后使用,也可以立即用于计算。例如, C 、 C ++、 Java 或者 C # 的程序员可以编写 Proj ectedJanSales = EstimatedSales ( January ); 来把调用函数 EstimatedSales 产生的结果(一月份共售出了多少小商品)赋值给变量 Pro j ectedJanSales 。 或者,程序员可以编写 if (LastJanSales ';/'-' m -'----;, T*-' J-—-— rP=^.-.V.Vi- 调用赴程的结果。此外,还有 一些飧 况是这样的,过粗表由一个事件'的发生激活 的,, 例如在 GUI 中 ,.有种过輊描述了当一个接钮被单击 的肘候 应该产生什么婢舞, 屢神袜 粗不是 :由 其他 程序单元调用激活的,:而是作为单击按钮这一事 件的结 果漱活:^。这程表通过事件而不 是明确的请求来激活的軟件系统称作事件驱动 ( event - driven ) 系统。 简言之,一个事件驱动 软件系统是由这样 碎过寒 组成,它個潘述各种事件发生时雇该做件幺。当系统执行时,这些 过程等待,直到与它们对应的事件发生,然后它们被激活,并在完藏它扪的任务后回到等待 状态。 问题与练习 1:•塵細域是 _ :也^ ..“.:' 的区别是仟么? ! 3. 为#么许多程序设计语言执行 I / O 操作的方式很像是调用过程? 4. 形参和实参的区别是什么? 5 . 当用一个现代程序设计语言写程序时,程序员都倾向于使用动词来命名过程,使用名词来命名函数, 为什么? 6.4 语言实现 在本节中,我们将研究把高级语言编写的程序转换为机器可执行形式的过程 6.4.1 翻译过程 将一个程序从一种语言转换为另一种语言的过程称为翻译 ( translation )。 原始形式的程序 称作源程序 (source program ) ,翻译后的版本称作目标程序 ( objectprogram )。 翻译过程包括3 部分工作,分别是词法分析、语法分析和代码生成,实现相应行为的单元分别称为词法分析器 (lexical analyzer )、 语法分析器 ( parser ), 以及代码生成器 (code generator ), 参见图6-13。 源程序 '词雄分析無 -^ : 議_懸_議’-1 标记; '.V 分析树 5-:: :■::;: i >" ■:;: :■::;: 图 6-13 翻译过程 代码 I 1 ■ I 、 | , ' '| I 卜 | Vi '/Lv' 目标程序 词法分析是识别源程序中构成单个实体——标记 '( token ) ——的符号串的过程。例如,3个 符号的153不应该解释成一个1、一个5和一个3,而是应该识别为一个数值。同样,程序中的一 个单词,尽管由独立的符号组成,也应该解释成一个单元。大多数人进行词法分析都是下意识 的。当要求大声朗读的时候,我们都是读出一个词来,而不是逐个读出单个字母。 因此,词法分析器逐个符号地读源程序,并且识别出哪些符号的组合可以代表一个标记, 并且根据它们是否是数、词、算术运算符等将这些标记分类。词法分析器对标记及其分类进行 编码,并将它们提交给语法分析器。在此过程中,词法分析器跳过了所有的注释语句。 194 第 6 章程序设计语言 因此,语法分析器将程序看做是由词法单元(标记)组成的,而不是由独立符号组成的。 语法分析器的工作就是将这些单元组合成语句。实际上,语法分析是标识程序中语法结构和辨 认每个成分作用的过程。正是语法分析技术使得人们在读句子 The man the horse that won the race threw was not hurt . 时,会停顿一下。(试一试这句话 :“ That that is is . That that is not is not . That that is not is not that that is , ” !) 为了简化语法分析过程,早期程序设计语言坚持每个程序的语句都要以一种特定的方式定 位在打印页上。这种语言称为固定格式语言 ( fixed - formatlanguage )。 今天,大多数程序设计语 言都是自由格式语言 ( free-format language ), 意味着不再苛求语句的位置安排了。从人的角度 来看,自由格式语言的好处在于程序员可以编写可读性更高的程序。在这种情况下,通常使用 缩进来帮助读者更好地把握语句的结构。对于一个程序员,不应该写 if Cost 球光:机祕拿,#么它赛被灰—士舆聲网中:的男:里巍:_轉^乐> : :夫些 情馮卞:心这來执衧是由轉♦释*_:, .: 褊典并攝广勢婧味下,,一机夢藤鉢械誠執 I ' 快途__ ,:途 雄为即时編铎⑸: I T ' ; : 6.4 语言实现 195 语法分析过程基于一系列语法规则,这些规则定义了程序设计语言的语法。总地来说,这 些规则称为文法 ( grammar )。 表达这些规则的一种方法是借助语法图 (syntax diagram ), 它是 程序文法结构的图形化表示。图 6-14 给出了第5章伪代码中的 if - then - else 语句的语法图。这 个图说明 if - then - else 结构以关键字 if 开始,然后是一个布尔表达式,接着是关键字 then , 随 后是一个语句。此结构的后面有没有 else 和语句都是允许的。注意,实际出现在 if - then-else 语句中的项都使用椭圆形框,而需要进一步描述的项,诸如布尔表达式和语句等,都在矩形 框中。需要进一步描述的项(矩形框中的)称为非终结符 ( nonterminal ), 而出现在椭圆形框中 的项称为终结符 ( terminal ) o 在一个程序设计语言语法的完整描述中,非终结符由额外的图表 描述。 图 6-14 if - then - else 伪代码语句的语法图 作为一个比较完整的例子,图 6-15 给出了一组语法图,它们描述了一个称为表达式(可以 是简单的数学表达式结构)的结构的语法。第一个图描述了一个表达式,这个表达式由一个项 ( term ) 组成,后面可以跟(也可以不跟)一个+号或-号,运算符后面跟有另一个表达式。第 二 个图描述了一个项,这个项由单个因子组成,或者由一个因子后面跟一个 X 号或+号,然后再 跟另一个项组成。最后一个图描述了一个因子,这个因子由 x 、 y 、 Z 中的一个字符组成。 项 1 , 磁子 . .1 lilfcliill j __ 因子 — I - ►© - ► 图 6-15 —个简单的代数表达式的语法图 一 个特定的串符合一组语法图的方式还可以用语法分析树 (parse tree ) 进行图形化表示, 根据图 6-15 中所示的语法图,图 6-16 描述了字符串 x+yxz 的语法分析树。注意,树的顶端以 非终结符表达式开始,并且在每一层都给出了本层的非终结符是如何分解的,这个过程直到获 得该串本身中的全部符号才结束。该图还特别说明 (根 据图 6-15 中的第一个图),一个表达式 196 第 6 章程序设计语言 可以分解成一个项,后面跟有+号,然后再跟一 个表 达式。 接着,项可以分解成(用图 6-15 中 的第二个图)一 个因子 (结果是符号 x ), 最后的 表达式可以分解(用图 6-15 中的第三个图)为 一个项(结果是 y x zh 语法分析过程本质上就是为源程序构建语 法分析树的过程。的确, 一 个语法分析树代表了 语法分析器对程序文法构成的理解。因此,描述 程序文法结构的语法规则是不允许同一个字符 串出现两个不同的语法分析树的,因为这将导致 语法分析器内部发生混乱。如果一个文法允许同 一个字符串有两个不同的语法分析树,我们称之 为多义文法 (ambiguous grammar ) 。 语法中的这种歧义是很细微的。事实上,图 6-14 中所示的规则存在这样的缺陷,对于下面的 语句可以生成如图 6-17 所示的两个语法分 析树: Z 图 6-16 基于图 6-15 的 x+y x z 字符串的语法分析树 if B7 then if ^then SI else S2 注意,这两个解释是明显不同的。第一个表示语句52在幻为假时执行,而第二个表示语句52 仅当 W 为真并且犯为假时执行。 辑鲺 B2 S1 语句 图 6-17 语句 if 5/ then if 犯 then 57 else W 的两个不同的语法分析树 6.4 语言实现 197 正式的程序设计语言的语法定义要避免这样的混乱。在伪代码中,我们通过使用括号来避 免这样的问题。我们可以写 \f B7 then (if 52 then 57) else S2 以及 if B1 then (if then SI else S2) 来区分这两种可能的解释。 当语法分析器分析一个程序的文法结构时,它能够标识单独的语句,并能够区分声明语句 和命令语句。当识别声明语句时,它将这^^声明的信息记录在一个符号表 (symbol table ) 中。 因此符号表中包含了诸如变量名等信息和与其对应的数据类型和数据结构的信息。然后,当分 析到 z 一 x + y ; 这样的命令语句时,语法分析器会以这些信息为依据来进行分析。特别是,为了确定符号+的含 义,语法分析器必须要知道 x 和 y 的数据类型。如果 x 是实型而 y 是字符型,将 x 和 y 相加是没有任 何意义的,并且将会报告 出错; 如果 x 和 y 都是整型,那么语法分析器将会请求代码生成器生成 相应的整数加法操作码的机器语言 指令; 如果 x 和 y 都是实型,那么语法分析器将会请求代码生 成器生成相应的浮点数加法操作码的机器语言指令;如果都是字符类型,语法分析器将会请求 代码生成器建立一串机器语言指令来完成相应的连接操作。 如果 x 是整型而 y 是实型这种特殊的情况,加法的概念是可用的,但是值不能以兼容的形 式编码。在这种情况下,语法分析器可能选择使代码生成器生成指令把其中的一个值转换为另 一种类型,然后再执行加法运算。这种类型的隐式转换称为 强制类型转换 ( coercion )。 许多程序设计语言的设计者都反对强制类型转换,因为隐式类型转换会改变数据项的值, 并因此导致微妙的程序 bug 。 他们认为,经常需要强制类型转换就意味着程序设计语言在设计. 上存在纰漏,因此不应该使用语法分析器来迁就。因此,大多数现代程序设计语言都 是强类 型 (strongly typed ) 的,这意味着一个程序请求的动作必须包含允许的数据类型,不允许强制 类型转换。 一些 程序设计语言(如 Java ) 支持进行强制类型转换,只要 它是类型提升 (type promotion ), 意思是将一个低精度值转换为一个较高精度的值。可能改变一个值的隐式强制类 型转换将被报告为错误。在大多数情况下,程序设计人员仍可以请求这种类型转换,方法是进 行显式强制类 型转换 ( typecast )。 显式强制类型转换会通知编 译器: 程序设计人员知道将应用 类型转换。 翻译过程的最后一步就是代 码生成 (code generation ), 它是生成机器语言指令以实现语法 分析器识别出的语句的过程。这个过程涉及许多问题,其中一个就是要生成效率高的机器语言 版本的程序。例如,我们考察语句 的翻译工作。如果这些语句作为两个独立语句来进行翻译,那么在执行加法操作之前,每一条 语句都需要数据从主存传送到 CPU 。 然而,效率可以通过这样的认识 获得: 一旦第一条语句被 执行之后, x 和 z 的值已经存在于 CPU 的通用寄存器中,所以执行第二条语句的时候就不再需要 198 第 6 章程序设计语言 从内存中读取这两个 数了。 这种方法就称作代码优化 (code optimization ), 它是代码生成器的 一 个非常重要的工作。 最后,我们应当注意的是,词法分析、语法分析和代码生成这3个步骤并不是严格按照顺 序执行的。相反,这些步骤是交织在一起的。词法分析器首先从源程序中读取字符,并且标识 出第 一 个标记。它将这个标记传送给语法分析器。每当语法分析器从词法分析器接收到一个标 记时,就开始分析读取的文法结构。此时,它可能会向词法分析器请求另一个标记,而如果语 法分析器认为已经读到了一个完整的短语或者语句,那么它就会请求代码生成器产生相应的机 器指令。每一个这样的请求都会使代码生成器生成机器指令,并且将其加入到目标程序中。将 一个程序从一种语言翻译成另一语言的工作很自然符合面向对象范型。源程序、词法分析器、 语法分析器、代码生成器以及目标程序本身都是对象,每一个对象都在实现自己的任务的同时 通过来回传递消息与其他对象进行交互(见图6-18)。 綱序 词法分析器 :細麵_ 语法 ■ :夕 mmm 图 6-18 翻译过程的面向对象方法 6.4.2 软件开发包 像编辑器和翻译器这样的在软件开发过程中应用的软件工具,通常组合成一个软件包,来 实现一个集成的软件开发系统的功能。根据 3.2 节中的分类框架,这样的系统属于应用软件。通过 使用该应用软件包,程序员可以很方便地在一个编辑器中编写程序,使用翻译器将程序转换成 机器语言,并且可以使用各种各样的调试工具来跟踪出错程序的执行,以发现哪里出现了问题。 使用这样的集成系统的好处很多,最明显的就是程序员在需要修改和测试程序时,可以很 容易在编辑器和调试工具之间来回倒换。此外,许多软件开发包允许开发中的相关程序单元以 合适的方式链接,以便简化对相关程序单元的存取。一些软件包还维护这样的记录,即在上次 基准制定以来,一组相关程序单元中哪些已经做了修改。这些功能对于许多相关程序单元是由 不同程序员开发的大型软件系统开发来说是非常有用的。 从小范围讲,软件开发包中的编辑器通常根据正在使用的程序设计语言进行定制。这样的 编辑器通常提供自动行缩进功能,这已成为目标语言事实上的标准。有时,对于关键字,只要 程序员键入前面少数几个字符,编辑器就能够识别并且自动补全。此外,编辑器可以突出源程 序中的关键字(也许使用颜色),使程序易读。 在第7章中,我们将学到软件开发人员越来越多地研究这样的方法,即如何用预制的称为 构件的程序块构建新的软件系统,这导致了一种新的称为构件架构的软件开发模型的产生。基 于构件架构模型的软件开发包通常使用图形界面,在监视器屏幕上由图标表示各种构件。在这 种环境下,程序员(或者构件装配人员)用鼠标选择所需要的构件。选好的构件可以用软件开 发包的编辑器进行定制,然后用鼠标进行定位和单击就可以加到其他构件上。这种软件包代表 6.5 面向对象程序设计 199 了在研究更好的软件开发工具的方向上前进了一大步。 问题与练习 X - 描述翻译过程的3个主要步骤。 2. 什么是符号表? 3. 终结符和非终结符的区别是什么? , , c 4:基于@6。15中的谱法图、为表达式妙 y 十 x + 含画出燒法 分析树 根据下面的语法图,描述達循语法结构 fflacha 的字符串。 Chacha : 6.5 面向对象程序设计 在 6.1 节中,我们看到面向对象程序设计范型必然需要开发称 为对象 ( object ) 的活动程序 单元,每一个对象都包含了描述对象怎样响应各种激励的过程。 一 个问题的面向对象解决方法 就是标识出涉及的对象,并将其作为一个独立的单元来描述。接着,面向对象程序设计语言提 供了描述对象及其行为的语句。在本节中,我们将引入一些以 C 科、 Java 和 C # 语言形式出现的 语句,这3种语言都是当今比较著名的面向对象程序设计语言。 6.5.1 类和对象 我们可以考虑开发一个简单的计算机游戏的任务,在这个游戏中,玩家要通过高能量激光 器向从天上掉下来的流星进行射击来保卫地球。每个激光器都有一定的内部能量源,而每一次 射击将消耗一部分能量。 一 旦能量用尽,激光器就失去了作用。每一个激光器都应该能响应瞄 准右面一点、瞄准左面一点或发射激光束的命令。 在面向对象范型中,计算机游戏中的每一束激光都作为一个对象来实现,每个这样的对象 都包含了它的剩余能量的记录以及修改目标和发射激光束的过程。既然所有的激光对象都有同 样的属性,它们可以使用一个公用模板来构建。在面向对象范型中,这样的一组对象的模板称 作类 ( class )。 在第8章我们将探究类和数据类型之间的相似点。现在,我们只需注意类描述的是一组对 象的共同特征,这与基本数据类型整型的概念包含像数字1、5和82这样的数的一般特征类似。 一 旦程序员在程序里面包含一个类的描述,那个模板就可以用来构建和操作那一 “类型”的对 象,这相当于基本的整型允许操作整型的“对象”。 200 第 6 章程序设计语言 _ 在 C ++、 Java 和 C # 语言中,类使用下列形式的语句来描述: class Name 其中,是一个名字,在程序的其他地方可通过这个名字来引用该类。括号中的是被描述的 类的属性。图 6-19 特别展示了描述计算机游戏中激光结构的名为 LaserClass 的类。这个类包含 1个名字为 RemainingPower 的整型变量以及3个名字分别为 turnRight 、 turnLeft 和 fire 的 过程的声明,这些过程描述了完成相应动作的执行步骤。因此,任何一个从该模板构建的对象 都包含如下特性:1个名字为 RemainingPower 的变量以及3个名字分别为 turnRight 、 turnLeft 和 fire 的过 程。 class LaserClass int E^mai'nihg^ower > =^100 VG 班 ― 豈讲 ! 识 igffett 1 ( 保留在每个该类型对 象里的数据的描述 描述该类型对象应该如 何应对各种消息的方法 图 6-19 描述计算机游戏中一种激光武器的类结构 对象内部的变量,例如 RemainingPower , 称为实例变量 (instance variable ) ,而在对象里 的过程称为方法 ( method , 对于 C ++ 来说称作成员函数)。注意,在图 6-19 中,实例变量 RemainingPower 使用类似于在 6.2 节中的声明语句来描述,而方法使用 6.3 节中的函数或者过程 的方式来描述。实例变量的声明和方法的描述是最基本的命令型程序设计的概念。 一旦在游戒程序中描述了类 LaserClass , 我们就可以声明3个 LaserClass “类型”的 变量 Laserl 、 Laser 2、 Laser 3 , 这是通过语句 LaserClass Laserl , Laser 2, Laser 3; 来实现的。注意,这与我们在 6.2 节中所学的声明3个整型变量 x 、 ¥和 2 的语句 int x , y , z ; 的格式是一样的。它们都包括了一个类型名,以及出现在类型名后的将要声明的变量列表。二 者都由变量名后面跟着将要声明的一系列变量组成。区别在于,后者所说的变量 X 、 ¥和2在程 序中用来指向一个整型项(基本类型),而前者所说的变量 Laserl 、 Laser 2 和 Laser 3 在程序 中用来指向一个 LaserClass “类型”的项(这是在程序中自己定义的“类型”)。 —旦我们声明了 LaserClass “类型”的变量 Laserl 、 Laser 2 和 Laser 3 ,就可以给它 们赋值。在这种情况下,所赋的值必须是与 LaserClass 类型相一致的对象。这些赋值可以通 过赋值语句来进行,但是在声明变量时,在同一个声明语句内给变量赋初值通常是很方便的。 在 C # 语言的声明中,这种初始赋值是自动的。也就是说,语句 LaserClass Laserl , Laser2 , Laser 3 ; 类描述 6.5 面向对象程序设计 201 不仅创建了变量 Laser 1、 Laser 2 和 Laser 3 ,而且还创建了3个 LaserClass “类型”的对象, 其中每一个作为每个变量的值。在 Java 和 C # 中,这种初始赋值与对一个基本类型变量赋初值的 方法基本相同。特别是,鉴于语句 int x = 3; 不仅声明了 一个整型变量 X , 同时还为这个新的变量赋值为3,语句 LaserClass Laserl 二 new LaserClass (■) ; 不仅声明了一个 LaserClass “类型”的变量 Laserl , 而且还通过 LaserClass 类模板创建了 一 个新的对象,并且将其作为初值赋给 Laserl 。 在进一步讨论之前,我们应该强调一下类和对象之间的区别。类是一个模板,对象是由 这个模板创建出来的。一个类能够用来创建许多个对象,我们经常将对象称为类(构建该对 象的类)的实例 ( instance )。 因此,在我们的计算机游戏中, Laserl 、 Laser 2 变量 的值和 Laser 3 都是 LaserClass 类的实例。 在用声明语句创建对象并且将其赋给变量 Laserl 、 Laser 2> La Se r 3 之后,可以通过 编写命令语句来激活这些对象(按面向对象术语来说,这称为 “ 向对象发送消息”)中合适 的方法。具体而言,我们可用赋给变量 Laserl 的对象通过以下语句执行 fire 方法: Laserl . fire (); 或者,我们可让赋给 Laser 2 的对象执行它的 turnLeft 方法,这是通过语句 Laser 2. turnLeft () ; 来实现的。这些实际上是过程的调用。的确,前面一个语句是调用赋给变量 Laserl 的对象内部 的过程(方法) fire , 后者则是调用赋给变量 Laser 2 的对象内部的过程 turnLeft 。 在这个阶段,流星游戏例子已经给出了掌握典型面向对象程序总体结构的背景知识(图 6-20)。它包含了与图 6-19 相似的一系列类的描述,每一个都描述了程序中使用的一个或多个对 象的结构。另外,程序会包含一个命令程序段(通常和名字 “main” 有关),这个命令程序段 包括了当程序运行时最初要执行的步骤序列 L 这个段包括与我们激光类的声明类似的声明语句, 用来建立程序中使用的对象,还包括调用那些对象中执行方法的命令语句。 aim 用 m 并调 称构适 常的合 C 象出 元对作 单示法 程指方 过它的 图 6-20 典型的面向对象程序结构 202 第 6 章程序设计语言 6.5.2 构造器 当构造对象时,通常需要进行一些个性化的定制。例如,在我们的电脑游戏中,有可能需 要实现一些具有不同初始能量设置的激光器,这就意味着不同对象中的 RemainingPower 实例 变量应该给定不同的初始值。这种初始化通过在合适的类中定义特殊的方法——构造器 ( constructor ) ——来进行,它是在构建类的对象时自动执行的。一个构造器在类的定义中是通 过使用与类名相同的名字来标识的。 图 6-21 给出了图 6-19 中的 LaserClass 类的扩展定义。注意,它包括一个构造器,该构造器的 形式是名为 LaserClass 的方法。这个方法将它接受的参数值赋给实例变量 Remaining Power 0 因此,当一个对象在类中构建出来时,这个方法就会执行,使得 RemainingPower 被初始化为 一个合适的值。 当 一个对象被创建时, class LaserClass / /构造器给: Remaining- { int RemainingPower; / Pcwer ■赋一个值 ;i| .;; : i : .::i ,: ::•;;•! i;: ;: ;.' .,:'••::,; : •!:: .'/i:- 1 : '' :. :: { -Lase^'Cla-ps . {in'itid;power), ■ '{ iriing©ower ; = -Tniti al Power ; } v void turnRight { } { ... } void turnLeft ( ) void fire { ) } 图 6-21 带有构造器的类 构造器所使用的实参是在引起构建该对象的语句里的参数列表中标识的。因此,基于图 6-21 中给出的类的定义, C ++ 程序员将会编写语句 LaserClass Laserl( 50 ) , Laser2{ 100 ) ; 来创建两个 LaserClass 类型的对象,一个是 Laserl , 初始能量值50,另一个是 Laser 2, 初始 能量值100。使用 Java 和 C # 的程序员完成同样工作的语 句是: LaserClass Laserl = new LaserClass(50 ); LaserClass Laser2 = new LaserClass(100 ); 6.5.3 附加特性 假设我们需要改进游戏,以使得玩家达到一定的分数时,可以获得奖励为其现有的激光器 补充能量。除了可以补充能量以外,这些徼光器与其他激光器有同样的属性。 为了简化对类似但不完全相同的对象的描述,面向对象语言允许一个类通过称为继承 ( inheritance ) 的方法包含其他类的属性。例如,假设使用 Java 来开发游戏程序,我们首先使 用之前描述的类语句来定义类 LaserClass , 这个类描述了程序中的所有激光器的共同属性。 接下来,我们使用语句 class RechargeableLaser extends LaserClass 6.5 面向对象程序设计 203 来描述另一 ■个类 RechargeableLaser 。 ( C ++ 和 C # 语言用冒号来代替 ext ends 。 ) 这里 ext ends 指明了这个类不仅继承了类 LaserClass 的特性,同时还包含了括号中出现的特性。括号可以 包含新的方法(名字也许是 recharge ), 用它描述重置实例变量 RemainingPower 为其初始值 的过程。一旦类定义好了,就可以使用语句 LaserClass La.serl , Laser2 ; 来将变量 Laserl 和 Laser2 声明为原来的激光器的变量,并且使用语句 RechargeableLaser Laser3, Laser 4 ; 来将变量 Laser3 和 Laser 4 声明为拥有类 RechargeableLaser 中描述的额外特性的激光器 变量。 继承的使用导致各种相似但是不同的对象的存在,也因此导致了在 6.2 节中讨论的重载的 现象。(让我们回忆一下,重载是使用一个诸如+的符号,_根据操作数类型的不同代表了不同的 操作。)假设一个面向对象的图形开发包包含各种对象,每一个都代表了一种形状(圆形、矩形、 三角形等)。 一 个特定的图像可能由一组这类对象构成。每个对象都有自己的大小、位置和颜色, 都有自己响应消息的方式,例如移动到一个新的位置或者在屏幕上画出自己。我们仅仅对图像 中的每个对象发送“画自己”的消息来画图。但是,对象形状的不同决定了所使用的画一个对 象的例程是不同的 ■ — 一 个正方形的画法和圆形的画法是不同的。这种对消息的自定义解释称 为多态 ( polymorphism ), 这种消息是多态的。 封装 ( encapsulation ) 是与面向对象程序设计相关的另一个特性,它是指限制对一个对象内 部属性的访问。说一个对象的特定属性是封装的,就意味着只有对象自己才可以访问它们。被 封装的属性称为私有属性,而可以从对象外部访问到的属性称为公有属性。 例如,让我们回到图 6-19 中的 LaserClass 类。它描述了一个实例变量 RemainingPower 以及3个方法 turnRight 、 turnLeft 和 fire 。 这些方法可以被其他程序单元访问,以使 LaserClass 的实例执行合适的动作。但是有关对 RemainingPower 值的修改应当只能由实例 内部方法来实现,其他程序单元不应具有直接访问这个值的能力。为了强制实现这些规则,如 图 6-22 所示,我们仅需要指定 RemainingPower 为一个私有变量而使其他3个方法都是公有 的。通过这种设计,在程序编译期间任何试图从对象的外部对 RemainingPower 的值进行访问 的操作都会被标识为错误,并要求程序员改正该错误。 ■!' ! : 0 > ? ! ■n'y ,":!k K ,: ':• ..•• :";::'• : - '.V : ' -V- .'. : uV..V- 卜 : i: ■: L:.: .i .i:. r .;• ,;.:, r ,,; ,r, .i^. - J;W I . h :V.. ,1:H. … J .V...i. 1 -:.: V JXv:, :; , r,', V-, -.'X : H's'lvl : •: S '; 1 i'' : : " ; .Y ',: ' : L J-J ?'': ,•••,! 'VX-,.v, • V • VV ! ; v,.-,H'•,:! :l :: .;. F iRr;r . ..•I 1 : • 士 .. J.U: 〜 .■: --4:; •'[!':: :: ;^:-:| ,': :',sv ; vr:-H rr.! - ,V |: :',:•;;. .:;«;' 卜 f〆 .. ...丄 _ .V •.. . : v _r … M.;."V... V". t .1 ; 人 .V . .., : - : \i : .V , :;•:. : 乂 : :,:••: 梟 fe ' k 邊 Sis 细轉寒可 喔議 1 S . S 繼議漏讓 園 i ; ' : 5 : ’ : T: . . . ' ’& ...... 一 .. 一 ' .一^ " ..:' 1 …… .. 204 第 6 章程序设计语言 类中的成分定义.为公有或 是私有依赖于是否要从其 他程序单元访问它们 图 6-22 在 Java 和 C# 中使用封装的 Lass er Class 的定义 *6.6 iij^iSi+4> 假设我们要为多路攻击敌人飞船的计算机游戏设计一个生成动画的程序。 一 个处理方法就 是只设计一个程序,用该程序来控制整个动画屏幕。这种程序将负责绘制每一个飞船(假设动 画做得很逼真),这意味着该程序将必须掌握许多飞船的各自特征。另一种方法就是设计一个控 制程序来控制单个飞船的动画,每个飞船的特征由参数决定,在程序执行的开始阶段给参数赋 值。然后,动画可以通过创建这程序的多个激活 ( activation ) 来构建,每一次激活都使用各 自的一组参数。同时执行这些激活,我们就会得到有许多飞船从屏幕上同时飞过的假象。 这种多个激活的同时执行称为并行处理 (parallel processing ) 或并发处理 (concurrent processing ) o 真正的并行处理需要多个 CPU ,. 每个 CPU 都执行一个激活。当仅有一个 CPU 可 用时,并行处理给我们的错觉是允许多个激活分享一个 CPU 的时间,其方式与通过多道程序设 计操作系统来执行相似(参见第3章)。 许多现代计算机应用程序在并行处理环境里解决要比在传统的单指令序列环境里更容易实 现。于是,较新的程序设计语言提供了表达并行计算所涉及的语义结构的语法。这种语言的设 计需要对这些语义结构的识别以及描述它们的语法的开发。 每一种程序设计语言都试图从自己的角度来处理并行处理范型,结果产生了不同的术 语。例如,“激活”这个非正式的称谓在 Ada 语言中称为任务 ( task ), 而在 Java 中称为线 程 ( thread )。 这就是说,在 Ada 程序中同时发生的动作是通过创建多个任务来执行的,而 在 Java 程序中是通过创建多个线程来执行的。在这两种情况下,结果都是多个活动被生成 和执行,其方式与多任务操作系统控制下的进程是一样的。我们将采用 Java 中的术语,把 这种“进程”都称为线程。 也许,在涉及并行处理的程序中必须表达的大多数最基本动作就是创建新的线程。如果希 望可以同时执行飞船程序的多个激活,那么就需要说明这一点的语法。这种产生新线程的处理 方法通常与请求一个传统过程的执行类似。不同之处在于,在传统的环境中,请求过程激活的 *6.6 程序设计中的并发活动 205 程序单元在所请求的过程终止之前不再往下执行(回想图6-8),而在并行环境中,请求程序单 元在被请求过程执行任务的同时继续向下执行(图6-23)。因此,要创建多个飞船飞过屏幕的场 景,我们可以写一个主程序,该主程序只生成飞船程序的多个徼活,每个激活都配有描述不同 飞船特征的参数。 调用程序单 元请求过程 调用程序单元 一 个与并行处理相关的更复杂的问题就是处理线程之间的通信。例如,在飞船例子中,代 表不同飞船的线程可能需要相互之间通告它们的方位以协调行动。在其他情况中,一个线程需 要等待,直到另一个线程到达了它计算中的某个位置,或者一个线程在实现特定的任务之前需 要停止另一个线程。 长期以来,这种通信需求一直是计算机科学家研究的课题,并且许多新的程序设计语言都 有不同的解决线程之间交互问题的方法。例如,考虑当两个线程操作同一个数据时面临的通信 问题。(这个例子在选读的 3.4 节更详细地进行了描述。)如果同时执行的两个线程都需要给一个 公用的数据项加3,需要一个方法来保证在允许一个线程#1行任务之前先允许另一个线程完成任 务,否则它们会使用相同初始值幵始各自的计算,这将意味着最后的结果将会是加3而不是加6。 一次只能由一个线程访问的数据称为必须互斥访问的资源。 一 种实现互斥存取的方法就是编写描述所涉及线程的程序单元,以便在一个线程正在使用 共享数据时,它可以阻止其他线程访问这个数据,直到这样的访问是安全的。(这个方法在 3.4 节中已经描述过,在那里我们把一个进程中访问共享数据的那部分标识为临界区。)经验表明, 这个方法是有缺陷的,它把保证互斥的任务分散在程序各处——每个访问该数据的程序单元都 必须正确设计以确保这种互斥,因此一个程序段中的错误就能使整个系统崩溃。因此,许多人 认为一个更好的解决办法是使数据有控制对自身访问的能力。简而言之,不再依赖于访问数据 的线程来防止多重访问,而是赋予数据本身这个能力,结果是访问控制集中于程序中的一个点, 而不是分散于许多程序单元中。增加了对自身访问的控制能力的数据项称作监控程序 ( monitor )。 我们看到,程序设计语言中对于并行处理的设计包括开发表达诸如线程的创建、线程的暂 停和重启、临界区的标识以及监控程序的组成等方法。 在结束本节时,我们应当注意,尽管动画提供了一个探索并行计算问题的有趣场景,但是这 仅仅是从并行处理技术中受益的许多领域中的一个。天气预报、空中交通管制、复杂系统(包括 核反应、行人的交通等)的模拟、计算机网络以及数据库的维护都是可以应用这项技术的领域。 206 第 6 章程序设计语言 : ^71.. I . :掃 ■鐘: 面向系#&备、移动设备和嵌入式^开東软件,通常使用在其娘使用的相同的 通用编程语:言。若有较大的键盘和额外爾舞心,我们可以单使用智蘢早某些智能手机 应用。 不过, 大多数情况下,智能手机尚魏件是使用特殊软件系统在寿裏 g 开发的,而这 些特殊软#系统则提供用于编辑 、•译 _试智:能手机软件的工具 * 薦用通常用 Java 、 C ++ 和 C # 福写。不过,若要编写较复用或核心系统软件,我们夕卜支持并行 处理和事件驱动的编程。 _ _ 问题与练习 _ , - _ 1. 可以进行并发处理的 程序设 计语言有哪些特胜是传統语言中没有的? 2. 描述两种可以确保对_据互斥访问的方法?.:二 3. 说出除了动画以夕卜可受益于并行计聋的其:他环璩。 :'!■ 4H i M*' ' r *6.7 说明性程序设计 在 6.1 节,我们断言形式逻辑提供了一个通用的解决问题的算法,围绕这个算法可以构建 一个说明性程序设计系统。在本节中,我们将研究这个断言,首先介绍这个算法的基本原理, 然后再简要地看一看基于这种算法的说明性程序设计语言。 6.7.1 逻辑推演 假设知道 Kermit 要么病了要么就在舞台上,并且被告知 Kermit 不在舞台上,我们就可以 推断出 Kermit 一定是病了。这个演绎推论的例子称 为消解 ( resolution )。 消解是一种称 为推理 法则 ( inferencerule ) 的许多技法之一,可以用来从大量的陈述中推导出结果。 为了更好地理解消解,我们首先可以用单个字母表示简单命题,通过符号 n 来表示命题的 否定。例如,我们用 J 来代表 “Kermit 是一位王子”,用5来代表 “Piggy 小姐是一位演员”, 那么,表达式 ^ OR ^ 意味着 “ Kermit 是一位王子或者 Piggy 小姐是一位演员”,而 B AND — *A 意味着 “Piggy 小姐是一位演员而 Kermit 不是一位王子”。我们将用箭头来表示蕴涵关系。例如, 表达式 A^B 意味着“如果 Kermit 是一位王子, Piggy 小姐就是一位演员”。 以这种通式,消解原理意味着从命题 P OR Q 和 R OR -.g 可以归结出命题 P OR R *6.7 说明性程序设计 207 这样,我们就说原来的两个命题消解形成了第三个命题,我们称之为消解式 ( resolvent ;^ 重要 的是要看到,这个消解式是原始命题的逻辑结论。这就是说,如果原始命题是真的,那么消解式 也一定是真的。(如果 g 是真的,那么7?—定是 真的; 如果2是假的,那么户一定是真的。因此,不 管2是真是假, P 或者 A —定是真的。) 如图 6-24 所示,我们用图形化的方法表示了这两个命题的消解,在这个图中,引自原始命 题的连线指向下面的消解式。注意,消解只能用于成对命题,并且这些命题以子句形式 (clause form ) 出现 • ~■也就是说,命题的基本组件是通过布尔运算符 OR 连接起来的。因此 POKQ 是子句形式,而 P-Q 就不是子句形式。这对于我们来说不是很重要,因为这是数理逻辑中的一个定理的推导结果,这个 定理说,任何以一阶谓词逻辑(一个用扩充的表达能力表示语句的系统)表达的语句都可以用子句 形式来表达。我们不在这里进一步探讨这个重要的定理,但是为了今后的使用,我们发现命题 P-Q 等价于子句形式的命题 2 OR ,尸 图 6_ 24 消解命题 CPORg) 和 (ROR 推出 CPORJO 如果 一 组命题中的所有命题不可能同时为真,那么这组命题就是不相容的 ( iaconsistent )。 换句话说, 一 组不相容的命题是一组自相矛盾的命题。一个简单的例子是命题尸与命题 iP 的组 合。逻辑学家已经证明,重复的消解提供了验证一个不相容子句的集合的不相容性的系统化方 法。这个方法就是,如果反复进行消解而产生了一个空子句(消解命题 P 和命题的结果), 那么原来的一组命题必定是不相容的。例如,图 6-25 证明命题集合 P OR Q R OR- 'Q ― 、R ― \P 是不相容的。 图 6-25 消解命题 ( POR2) 、 (及 OR^QX ,及和 iP 208 第 6 章程序设计语言 假定我们要证明一组命题蕴涵了命题尸。推导这个命题尸就相当于对命题一^取反。因此, 我们所需要做的就是将原来的命题与 一 P 进行消解,直到产生一个空子句。基于获得的空子句, 我们就可以说 iP 与原来的命题组是不相容的,从而可以推导出原来的一组命题一定蕴涵户。 在将消解应用于一个实际的程序设计环境之前,还有最后一个问题。假设我们有两个命题 (Mary is at X) — (Mary's lamb is at X) (其中 X 代表任何地方)和 Mary is at home 按照子句形式,这两个命题变为 {Mary" s lamb is at X) OR —«(Mary is at X) 和 (Mary is at home) 乍一看,似乎没有可以消解的元素。另 一 方面,元素 (Mary is at home ) 和 一《 (Mary is at X ) 近于相互对立。问题是要认识到,命题 Mary is at X 是一个关于位置的通用命题,而 关于 home 的命题则是它的特殊形式。因此,对于第一个命题的特殊情 况是: (Mary' s lamb is at home) OR —«(Mary is at home) 它可以与命题 (Mary is at home) 消解产生命题 (Mary 1 s lamb is at home) 把值赋给变量(例如将 home 赋给 x ), 从而使得消解可以进行的过程称为单一化 ( unification )。 这个过程使得演绎系统中一般的命题可以用于特定的应用。 6.7.2 Prolog 程序设计语言 Prolog ( PROgramminginLOGic 的缩写)是一个说明性程序设计语言,它解决 问题的基本算法就是反复地进行消解。这样的语言称为逻辑程序设计 ( logic programming ) 语言。 一个 Prolog 程序由一组初始语句组成,基本的算法在它们之上进行演绎推理。构成这些语句的成 分称为谓词 ( predicate )。 一 个谓词由一个标识符和一个带括号的语句组成,括号里列有该谓词的 变元。谓词代表了与它的变元相关的事实,因而谓词标识符的选取通常都反映该事实的基本语义。 因此,如果希望表达 Bill 是 Maiy 的家长,我们可以使用这样的谓词 形式: parent(bill , mary) 注意,尽管这个谓词里的变元表示正常的名字,但是它们是以小写字母开始,这是因为 Prolog 区分常量和变量的依据是常量以小写字母开始,而变量以大写字母开始。[这里,我们使 用了 Prolog 的专有名词,用术 语常量 ( constant ) 代替更通用的术语 字面量 ( literal )。 更准确一 点讲,在 Prolog 里用名词 bill (注意是小写)表示的字面量可能会被更通用的表示法表示为 Bill 。 名词 Bill (注意 B 是大写)在 Prolog 中表示变 量 。] 一个 Prolog 程序中的语句有事实和规则两种,每个都是以一个句点来结束。一个事实包括 一个谓词。例如,乌龟比蜗牛快这个事实用 Prolog 语句可以这样来 描述: faster{turtle , snail). 而兔子比乌龟快的事实可以这样来 表示: faster(rabbit , turtle). *6.7 说明性程序设计 209 一个 Prolog 规则是一个“蕴涵”语句。但是,: Prolog 程序员并不是将语句写成像这 样,而是写成 “ HfZ ” 这样,除非使用符 号:- (一个冒号和一个连字符)来替代符号 if 。 因此 规则 “X is old implies X is wise ” 对于 一 个逻辑学家来说可能要这样来表述: old (X) —wise (X) 而在: Prolog 语言里表示为: wise (X): - old (X) . 又如,规则 (faster (X, Y) AND faster (Y, Z) ) — faster {X, Z) 在 Prolog 里表 达为: faster(X, Z) :- faster(X, Y) ,faster {Y f Z) . ■ 这个分隔 faster (X,Y) 和 faster CY 的逗号代表合取符 AND 。 尽管这样的规则不是子 句形式,但是它们在 Prolog 里是被允许的,因为它们很容易转化为子句形式。 记住, Prolog 系统并不知道程序中谓词的意义,它只是根据消解法则以完全符号的方式来对 语句进行操作。因此,用事实和规则来描述谓词的有关特性完全是程序员的职责。从这一点来看, Prolog 的事实倾向于用来标识谓词的特殊实例,而规则用来描述一般的法则。这就是前面有关谓 词 faster 的语句所使用的方法。这两个事实描述了 “快”的特定实例,而规则描述了一个一般 的属性。注意,兔子比蜗牛快的事实,尽管没有明说,但这是两个事实通过规则结合的结论。 当使用 Prolog 语言进行软件开发时,程序员的工作就是开发一组事实和规则来描述已知的 信息。这些事实和规则构成了要在演绎推理系统中使用的初始语句集合。一旦这个语句集合确 定了,那么可以向系统建议一些猜测(在 Prolog 术语中称为目标)——通常通过键盘输入它们。 当这样的一个目标向 Prolog 系统提交后,系统利用消解来试图证明这个目标是初始语句的推导 结果。基于描述 faster 关系的一组语句,下面的每一个目标 faster(turtle, snail). faster(rabbit , turtle). faster(rabbit, snail). 都可以证明,因为每一个都是初始语句的逻辑结果。最开始的两个与出现在初始语句中的事实 相同,而第三个需要系统的某种程度上的演绎。 如果我们提供的目标的变元不是常量而是变量,那么可以得到更为有趣的例子。在这种情 况下, Prolog 试图从初始语句中推导出 目标, 同时跟踪推导所需要的单一化。然后,如果这个 目标达到了,那么 Prolog 将报告这些单一化。例如,考虑 目标: faster(W, snail). Prolog 对于它的响应是 报告: faster(turtle, snail) ■ 的确,这是初始语句的一个结论,并且通过单一化与送个目标一致。此外,如果要求 Prolog 提 供更多的结论,那么它会找到并报告下面的 结论: faster(rabbit , snail). 而我们能通过提出目标 faster(rabbit , W). 210 第 6 章程序设计语言 要求 Prolog 寻找一些比兔子慢的动物的实例。事实上,如果我们以目标 faster (V, W). 开始, Prolog 将会报告所有可以从初始语句中推导出来的 f as ter 关系。这意味着一'个简单的 Prolog 程序可以用来证明某种特定的动物比另一种快,找出那些比某种给定的动物快的动物, 找出那些比某种给定的动物慢的动物,或者找出所有 faster 关系。 这个潜在的多功能性是激发了计算机科学家想象的特性之一。遗憾的是,当在 Prolog 系统 中实现时,消解过程显示了它理论形式中并没有呈现出来的限制,因此 Prolog 程序可能不能满 足它被预期的灵活性要求。为便于理解,首先注意图 6-25 中的示意图只显示了与手头任务相关 的那些消解,当然还有其他一些消解方式。例如,最左和最右子句的消解可产生消解式 Q 。 这 样,除了描述应用所涉及的事实和规则的语句外, Prolog 程序经常必须包含额外的语句,这些 语句的作用是正确地指导消解过程。由于这个原因,实际的 Prolog 程序可能不能获得我们先前 例子所暗示的目的多样性。 : ' |;:|: :::::•[:';::■; •: r - :: : : :;:i ;• : : :: : ii ;: i : •:; ;:••:;■ ■ i .'..:.: ::: •: •; ! : " i : - -:-.•:-:•;• 1 ;; . - ... : :: . :;:;| !: - ;;':: : .; : : i .: :: . , ,. , ' |: : ; . ' .j;: v . . : r :' : . i ^ , - : . 1. 语句 R、-S、T' 中哪一个是 (-^RORTOKS^ . (-^ORF) 、 (-iKORJR) . CUOR^S ). dORt/) 和 (S OEL n 构成的集合的逻辑结果? 2. 下面的语句 集合是 相容的吗 ? 为什 4? ■ , , PORgOR^ ,R0RQ ROK^P ,Q 3 ■• 完成下面的 Prolog 程序末 g 的两个规则,以使得谓词 motheWX, Y) 的含义 是 _1 是 Y 的母亲”,而 表示 “ 父是_父亲 ” 6 female(sue) . r male(bill ) . male ( j Qhnj , _ , ; 乂 .纏,誠 |:|j 每轉 4 .藏 i , :士: :.: :: r ...: : ••• . V:. . V -1 ■ . • _• . " parent{sue, carol ) . mother (X, Y:) :- ; . !; ; .:. ■為.令襄 , ::龜,: ... mh . ; : ;!;;::::■,■ : ::; : : ;; : ; s ;:; ::,:,: ; ::, : ,- i : .,^1;:;^ :■: , ;i ..;:..::,;,;., 5 then goto 80 X 二 X + 1 goto 90 80 X - X + 2 90 stop 24. 为了实现下述每个活动,概述命令型语言和面 向对象语言中的基本控制结构。 a . 判断将要执行哪一条命令。 b . 重复一组命令。 c . 改变变量的值。 25. 概述翻译器和解释器的区别。 26. 假设程序中的变量 X 声明为整型。当执行语句 X —2.5 时,会发生什么错误? 27. 说一个程序设计语言是强类型的意味着什 么? 28. 为什么一个大的数组不太可能通过按值传递 的方式传递给过程? 29. 假设过程 Modify 用第5章的伪代码定义为 212 第 6 章程序设计语言 _ procedure ModifyOO Y— 7; 打印 Y 的值 如果参数是按值传递的,当执行下面这个程序 段时,会打印出什么结果?如果参数是按引用 传递呢? X— 5; 对 X 应用 Mod ify 过程; 打印 X 的值; 30. 假设过程 Modify 用第 5 章的伪代码定义为 procedure Mod ify(Y) Y— 9; 打印 X 的值; 打印 Y 的值; 假设 X 是全局变量。如果参数按值传递,那么 执行下面的程序段会打印出什么结果?如果 参数是按引用传递呢? X— 5; 对 X 应用 Modify 过程; 打印 X 的值; 31. 有时,把实参传递给一个过程时是通过产生一 个副本给过程使用(如按值传递时)实现,但 在过程完成时,过程的副本里的值在调用过程 继续执行之前传递给实参。在这种情况下,称 参数是按值-结果传递的。如果参数按值-结果传 递,那么第 30 题的程序段会打印出什么结果? 32. a. 按值传递相对于按引用传递有哪些优点? b. 按引用传递相对于按值传递有哪些优点? 33. 语句 X —3+2 x 5 存在什么歧义? 34. 假设一个小公司有 5 名雇员,并且计划增加雇 员数目到6名。假设下面的赋值语句是该公司 —个程 序中的 语句: DailySalary= TotalSal/5 ; AvgSalary - TotalSal/5; DailySales = TotalSales/5 ; AvgSales = TotalSales/5 ; 那么,如果原程序使用了 NumberOf Emp 和 Workweek 两个常量(值都为 5) ,如何简化更 新程序的任务才能使赋值语句可表 达为: DailySalary = TotalSal / DaysWk ; AvgSalary = TotalSal/NumEmpl ; DailySales = TotalSales/DaysWk ; AvgSales 二 TotalSales/NumEmpl ; 35. a . 形式语言和自然语言之间的区别是什么? b . 分别举例。 36. 用语法图来表示第5章的伪代码中的 while 语 句的结构。 37. 设计一组语法图来描述你所在区域的电话号 码的语法。例如在美国,电话号码由分区电 话号码、地区电话号码和一个4位数字组成, 如 (444) 555-1234。 38-设计一组语法图来描述你的母语里的一个简 单的句子。 39. 设计一组语法图来描述不同的表示日期的方 法,如“月/日/年”或是“月/日,年” 40. 设计一组语法图来描述下述句子的文法结 构:在 yes 的后面有与 yes 个数相同的 no 。 例 如句子 “yes yes no no ” 符合要求,而句子 “no yes”、“yes no no ” 和 “yes no yes ” 就不满足 要求。 41. 有一种句子是这 样的: 在 yes 的后面有与 yes 个 数相同的 no , 在其后又有相同数目的 maybe , 例如 , rt yes no maybe”、“yes yes no no maybe maybe ” 就是这样的句子,而 “yes maybe ”、 “yes no no maybe maybe”、“maybe no ” 都不 是。给出一个论据说明这种句子的文法结构的 语法图的集合不能被设计出来。 42. 写一个句子描述下面语法图所定义的字符串的 结构,然后画出字符串 xxyxx 的语法分析树。 43. 为 6.4 节中的问题5增加语法图,以得到一个定 义 Dance 结构为 Chacha 或者为 Waltz 的一组语 法图,其中 Waltz 包括一个或多个以下模式的 副本: forward diagonal close 或 backward diagonal close 44. 基于图 6-15 中的语法图,为表达式 xXy+y + x 画出语法分析树。 45. 当为下面的语句生成机器代码时,代码生成器 可以实现哪些代码优化? if (X = 5) then (z X + 2) else (z — X + 4) 46. 简化下面的程 序段: 社会问题 213 Y — 5; if(Y = 7) then (Z — 8 ) else(Z 一 9) 47. 简化下面的程 序段; while (X not equal to 5 )do (X—5) 48. 在面向对象程序设计环境中,类型和类有哪些 相似,又有哪些不同? 49. 描述不同类型建筑的类的开发是如何使用继 承的? 50. 在类中私有部分与公有部分的区别是什么? 5 L a . 给出一个实例变量应该是私有的例子。 b . 给出一个实例变量应该是公有的例子。 c . 给出一个方法应该是私有的例子。 d . 给出一个方法应该是公有的例子。 52. 说明在模拟酒店门厅里行人交通时可能需要 的一些对象以及某些对象需要实现的动作。 *53. 在程序设计语言环境中,术语“监控程序”指 什么? *54. 并发处理的什么属性使它需要使用支持并发 的程序设计语言? *55. 画一个表示消解的图(类似于图 6-25) ,来说 明语句 (2 OR 、 CTORT ?) 、 丰土会问题 (尸 OR— 和 (POR-0) 的集合是不相容的。 *56 .语句 iR 、 CTORR ) 、 (尸 OR , g)、(POR 一 ■D 和 (及 OR -■ 尸) 的集合是相容的吗?解 释你的答案。 *57. 扩展 6.7 节中问题3和4描述的 Prolog 程序,以包 含另外的家庭关系,如叔叔、阿姨、祖父母和 堂兄弟。还要增加定义 parent ( X , Y , Z ) 的 规则 , parent ( X , Y, Z ) 的意 思是: X 和 Y 是 Z 的父母。 *58. 假设下列 Prolog 程序的第一条语句意味着 “ Alice 喜欢运动”,翻译程序中的最后两个语 句。然后,基于这个程序,列出 Prolog 将能得 出的 Alice 喜欢的所有事情。 likes(alice,sports). likes (.alice,music). likes(carol,music). likes(david,X) :- likes(X,sports). likes(alice,X) :- likes(david,X). *59. 如果下面的程序段在一台用 1.7 节中描述的 8 位浮点格式表示数值的机器上执行,会遇到什 么问题? X — 0.01; while (X not equal to 1.00) do (print the value of X ; X — X + 0.01) 下面的问题有助于分析一些与计算领域相关的伦理、社会和法律问题。回答这些问题不是 唯一的目的,还应该考虑为什么这样回答,以及你的判断是否对每个问题都标准如一。 1. 通常,版权法支持与一个想法的表达有关的所有权,而不是这个想法本身。因此,一本 书中的段落是受版权法保护的,但是这一段落表述的思想就不受保护。这种权利如何应 用到源程序和它表达的算法呢? 一 个了解某商业软件中所使用的算法的人,应当在多大 程度上被允许编写表达这些相同算法的程序,并将这个软件推向市场? 2. 通过使用高级程序设计语言,程序员可以使用诸如 if 、 then 和 while 这样的单词来表达 算法 D 计算机理解这些词的含义到了什么程度?正确应对这些词的使用的能力是否意味 着对词语的理解?你怎么知道另一个人理解了你所说的? 3. —个开发新的有用的程序设计语言的人应当有从这个语言的使用中获利的权利吗?如果 有,如何保护这样的权利? 一种程序设计语言可以在多大程度上被拥有?公司对雇员创 新的智力成果有多大程度上的所有权? 4. 在临近最后期限时,一个程序员打算放弃用注释语句编制文档以使程序能够按时完成, 这可以被接受吗?(初学者在得知文档对于专业的软件开发人员是如何重要时,往往非 常惊讶。) 5. 许多程序设计语言的研发致力于开发出这样的语言,它可以使程序员编写出人类易读且 容易理解的程序。在多大程度上应当要求程序员使用这些能力?也就是说,对于能够正 214 第 6 章程序设计语言 确实现功能但从人的角度看来写得不好的程序,在什么程度上才算是好程序? 6. 假设一个业余程序员写了一个程序供自己使用。这个程序没有使用程序设计语言的易读 特性,它也不够高效,并且包含了利用特殊情况(这个程序员试图使用这个程序的特殊 环境)的省事方法。此后,这个程序员把他的程序复制给希望使用这个程序的朋友,而 他的朋友又把这个程序复制给了他们的朋友。这个程序员要为他的程序可能出现的问题 负多大责任? 7. 计算机专业人员对于各种程序设计范型应该精通到什么程度?某些公司坚持在公司的所 有软件开发中都使用同一种预先确定好的程序设计语言。如果某计算机专业人员在这种 公司工作,你对前面问题的回答是否会发生变化? 课外阅读 Aho, A. V., M. S. Lam, R. Sethi, and L D. Ullman. Compilers : Principles , Techniques , and Tools , 2nd ed. Boston , MA: Addison-Wesley, 2007. Bames , J. Programming in Ada 2005, Boston, MA: Addison-Wesley, 2006. Clocksin, W. F. and C. S. Mellish. Programming in Prolog , 5th ed. New York: Springer-Verlag, 2003. Friedman, D. P., and M. Felleisen. The Little Schemer , 4th ed. Cambridge, MA: MIT Press, 1995. Hamburger, H. and D. Richards. Logic and Language Models for Computer Science . Upper Saddle River, NJ: Prentice-Hall, 2002. Kemighan, B.W., and D.M. Ritchie. The C Programming Language , 2nd ed. Englewood Cliffs, NJ: Prentice Hall, 1988. Metcalf, M. and J. Reid. Fortran 90/95 Explained , 2nd ed. Oxford, England: Oxford University Press, 1999. Pratt, T. W. and M. V. Zelkowitz. Programming Languages , Design and Implementation , 4th ed. Upper Saddle River, NJ: Prentice-Hall, 2001. Savitch, W. Absolute C++, 3rd ed. Boston , MA: Addison-Wesley, 2008. Savitch, W. Absolute Java , 3rd ed. Boston , MA: Addison-Wesley, 2008. Savitch, W. Problem Solving with C++, 6th ed. Boston, MA: Addison-Wesley, 2008. Scott, M. L. Programming Language Pragmatics , 3rd ed. New York: Morgan Kaufinann, 2009. Sebesta, R. W. Concepts of Programming Languages y 9th ed. Boston, MA: Addison-Wesley, 2009. Wu, C. T. An Introduction to Object-Oriented Programming with Java , 3rd ed. Burr Ridge, IL: McGraw-Hill, 2008. 软件工程 t 章讨论的是在开发大型的复杂软件系统过程中遇到的问题。之所以将这门学科称为 软件工程, 是因为软件开发是一个工程化的过程。研究软件工程的目标就是要找到一 种原则,能够指导软件开发过程,进而生产出高效的、可靠的软件产品。 本章内容 7.1 :软件工程学科 ' 7.2 _软件生命淘期 73 软件工:程方法 7.4 模块化 7.5 行业 工具, • •::;!.:! ::; i : 访:;_」杉.强皆七沙 mmmmmmmmirn 7.9 軟仵所本相本貴任 复习题 ___麵_|1__^ 以:: s.. 软件工程是计算机学科中的一个分支,致力于寻找指导大型复杂的软件系统的开发原则。 开发这类系统所面对的问题并非只是编写小程序所面对问题的放大。比如说,开发大型系统的 时候,要求许多人工作很长时间,而在这期间,预期的系统需求可能会改变,参与该项目的人 员也可能会变动。因此,软件工程包括了诸如人员管理和项目管理之类的主题,这样的主题更 多与业务管理相关,而不是与计算机科学相关。当然,我们的侧重点还是放在那些与计算机科 学密切相关的主题上。 7.1 软件工程学科 _ 为了有助于理解软件工程中涉及的问题,这里可以想象构造一个大型的复杂设施(一辆汽 车、 一 幢多层办公大褛或者一座教堂),对此进行设计,然后监管其构建过程。如何估算完成该 项目所需的时间、费用以及其他资源?如何把项目分割成几个便于管理的模块?如何保证构建 的模块相互协调一致?如何使工作在不同模块的人员相互沟通?如何衡量进度?如何妥善处理 更广泛的细节问题(如门把手的选择、壁饰的设计、彩色玻璃窗的蓝色玻璃的需求量、柱子的 强度、供暖系统的管道设计等)?在一个大型软件系统的开发过程中,同样需要面对如此繁多 的问题。 有人也许会这样认为,工程是一个很成熟的领域,因此一定会有大量现成的工程技术可以 用来解决软件工程中的这些问题。这种推理有一定的道理,但是忽略了软件的特性与其他工程 领域特性之间存在着的本质上的不同。这些差别已经影响了软件工程项目,导致了其花费增加、 推迟交付软件产品和软件产品不能满足用户的需求等后果。所以,在发展软件工程学科上,首 先要做的工作是弄清这些差别。 216 第 7 章软件工程 差别之一涉及通过常用的预先定制的构件来构建系统的能力。一些传统的工程领域已经长 期受益于这种能力,即在构建复杂的设备时,釆用各种现成构件。例如,设计一辆新车时,没 有必要重新设计引擎和传感器,利用这些构件以前的设计方案即可。然而,软件工程在这一点 上却是很落后的。过去,以前设计的软件构件一般用于特定的领域。也就是说,这些构件本质 上是为专门的应用设计的,所以将它们作为通用构件来使用是受限的。因此,复杂的软件系统 历来都是从头做起。正如在这一章中我们将看到的那样,在这一点上已经取得了重要的进展, 尽管还有很多工作要做。 软件工程与其他工程学科间的另一个差别在于缺少度量技术—— 度量学 ( metrics ) ——来 衡量软件的属性。例如,为了计算开发一个软件系统的费用,人们希望能够估算出预期产品的 复杂度,但是软件的复杂度估算方法还不太成熟。同样,评价软件质量的方法现在也不太成熟。 对于机器设备,质量的重要量度是平均无故障时间,这是对设备耐损耗性的一个基本衡量指标。 相反,软件没有损耗,所以这种方法在软件工程中并不适用。 软件指标不能以定量的方式测量,这也是软件工程和机械、电子工程不同,至今还未找到 一个严格、坚实的立足点的主要原因。这些早些的学科(如机械和电子工程)是建立在成熟的 物理学科的基础上的,然而软件工程仍然在找寻其自身的根基。 因而,现在的软件工程研究在两个层面上 进行: 一部分研究者(有时也称为实践派)的工 作指向开发直接应用的 技术; 另一部分研究者(称为理论派)则致力于探寻软件工程的基础原 理和理论,为将来构建更坚实的技术而努力。基于自身的原因,实践派以前开发和提出的许多 方法已经被其他方法代替,新的方法可能也将随着时间的推移而淘汰。与此同时,理论派的进 展也是一直很缓慢。 对实践派和理论派的两方面的进展需求是巨大的。我们这个社会已经沉迷于计算机系统及 其相关的软件。我们的经济、保健、政府、法律实施、交通运输以及国防系统等都依赖于大型 的软件系统。然而,在这些系统中,可靠性依然是最主要的问题。软件错误已经导致了一些大 的灾难,新近的灾难如月亮的升起被误以为是核攻击、纽约银行造成的一天损失500万美元、空 间探测器的失踪、过量的辐射导致人员的伤残,还有电话通信在同一时间大面积的瘫痪等。 这并不是说情况都很悲观。我们已经在解决诸如缺少预制的构件和衡量标准等问题方面取 得很多进展。此外,由于计算机技术在软件开发过程中的应用,导致了称为 CASE ( Computer - Aided Software Engineering , 计算机辅助软件工程)的出现,这使软件开发流程化,从而简化了 软件的开发过程。 CASE 已经促进了许多计算机化系统的发展,这些系统称为 CASE 工具 (CASE tool ), 包括项目设计系统(用来辅助经费预算、项目调度以及人员分配等)、项目管理系统(用 来辅助监控项目的开发进度)、文档工具(用来辅助编写和组织文档)、原型与仿真系统(用来 辅助开发原型系统)、界面设计系统(用来辅助图形用户界面的开发)、编程系统(用来辅助编 写和调试程序)等。其中一些工具的功能和字处理程序、电子制表软件、电子邮件通信系统等 差不多,最开始是为一般的应用开发的,已为软件工程师所采用。另外的一些工具主要是为软 件工程环境专门定制的复杂软件包。实际上,称为 IDE (Integrated Development Environment , 集成开发环境)的系统把软件开发工具(编辑器、编译器、调试工具等)组合到单个集成的程 序包中。为智能手机开发应用的系统便是这类系统的典型代表。这类系统不仅提供编写和调试 软件所必需的编程工具,而且提供模拟器(借助于图形显示)让程序设计人员查看正在开发的 软件在手机上的实际执行情况。 除了研究人员,专业人士和标准化组织(包括 ISO ) 的努力,美国计算机协会 ( ACM ) 和 美国电气及电子工程师协会 OEEE ) 也已经加入到改善软件工程状态的挑战中。这些努力 包括: 采用职业行为规范和道德规范来增强软件开发人员的职业精神,反对对个人职责的漠视 态度; 7.2 软件生命周期 217 建立衡量软件开发组织质量的标准,提供帮助这些组织改善它们标准的指导方针。 議霧感3嘴巧. _ 美国计^机协会 ( ACM ) 成立于1947年,是致力于推动艺术、科学及信息技术应用的国 际性科学与教育组袭,其总部在纽约,下设许多专亚的工作组 ( SIG ), 夯别致力于计算机体系 结构、乂工智能、幕物医学计算、计算机与社会、计算机科学教育、计算机图形_>超文本/ ;__体,, 操侧囊 ___編編: .難 _.麟_論_學。 AC 聲戀麵義 ■-■ L ,-- - f 續 v,w 」 -■ • jgw '-^ wVi " - " A ' '-" -• . •■- ■ :: J ^ - :- 美国电气及电子工程师协会(正6&读作 “ i - triplp - e ”) 是一个电气电子和制造工程师 的组织, 〔成 立于1 9 IS 3 年,由美釋电气工程师协会 (:由 包括爱迪生在内的电气工程师于1884年 儀 _): :、纏_ 泰灘________ :: ■■: : i :!: .; !; , i : ,:: 11 ■■:,::;■ ':'.' " ;:, f ji : " :;:; ": ,; ' : ' : ;! ii .: : .";: U .:. l :. |;. : !^!: i :^ :: ;:!:!:;..;:;: : ^;. io ;^.:;:.^ : !:.;: r :'...:.!:: :; ::::;: ;: :: :;.: :: : .:.: - : i : ! : ■' ' :: ; : ::': i ,::;:: ■ ;, :|; :! !:■: :: ! ; :;■ ■:■ ,!■ : ::: !: U ... i : l . k . : . ii . : ; :: : V " ..二 ...... _ 麵 賴慧 麵:稱 :的義_.蜂舞是讀納娜瓣細如 4 tol D ., : 本章的其余部分将讨论软件工程的一些基本原理(如软件的生命周期和模块化等),预测软 件工程发展的一些动向(如设计模式的定义与应用以及可复用软件构件的出现等),以及考察面 向对象范型对这个领域产生的影响。 问题 与练匀 1,汝什么 h 个程序沪的代码哲的_并非是对程茂菜舞 ft 的一钟好昀 輿量? ^ 朴么样 is 敎术能用__萣一今软#单充中奪多少错读? 4. 列举出两个在软件 IT 程领域已经或当前正在改善的应用环境。 7.2 软件生命周期 软件工程最基础的概念就是软件生命周期。 7.2.1 周期是个整体 图 7-1 表示的是软件的生命周期。这个图表明了一个事实,即软件一旦开发完成,它就进入 了一个既被使用又被维护的循环,这个循环将永不停止,直至软件生命周期结束。这种模式在 许多工业产品中很常见。不同之处在于,对于其他产品,维护阶段往往是一个修复过程,而对 于软件,维护阶段往往包括改错和更新。实际上,软件进入维护阶段,是由于以下的原 因:发 现了错误,软件应用中发生的变化需要在软件中做相应的修改,或者上一次修改中的变更导致 软件中其他地方出现了问题。 218 第 7 章软件工程 图 7-1 软件的生命周期 无论软件因为什么样的原因进入维护阶段,这个过程都要求人员(通常不是原作者)研究 底层的程序及其文档,直至把这个程序(或者至少是程序的相关部分)理解清楚。否则,任何 的改动只会带来更多的问题。即使软件设计精良并有良好的文档,要达到这种理解也是一件困难 的事情。事实上,到了这个阶段,软件的某部分往往会因为从头开发一个新系统要比成功修改现 存的软件包要更容易这样一个借口而弃之不用 ( 这个借口通常是真实的)。 经验表明,在软件开发期间稍作努力,就可能会在需要对软件进行修改时产生很不同的后 果。例如,在第6章对数据描述语句的讨论中,我们可以看出使用常量与使用字面量相比会大 大简化未来的修改工作。结果是,软件工程的大部分研究工作集中在软件生命周期的开发阶段, 以利用这种付出与收益之间的杠杆作用。 7.2.2 传统的开发阶段 软件生命周期传统开发阶段的主要步骤是需求分析、设计、实现和测试(参见图7-2)。 图 7-2 软件生命周期的传统的开发阶段 1 • 需求分析 软件生命周期的开发阶段从需求分析开始,其主要目标是确定预期系统要提供的服务,这 些服务的运行条件(如时间限制、安全性等),以及定义外界与系统的交互方式。 需求分析包括来自预期系统的利益 相关者 ( stakeholder , 将来的使用者,还有其他有关联的 人,比如法律上或者财务上相关的人)提供的重要数据。事实上,如果终端用户是一个实体(如公 司或政府机构),他们会为软件项目的实际执行雇用软件开发者,那么需求分析可能开始于用户独 自进行的可行性研究。在其他一些情况下,软件开发者可能为大众市场生产 COTS (Commercial Oflf - Hie - Shelf , 商用现成产品)软件,这些软件或许在零售商店销售,或许可通过因特网下载。在 这种情况下,用户不再是准确定义的实体,需求分析可能要从软件开发者的市场调研开始。 在任何情况下,需求分析过程都 包括: 编写和分析软件用户的 需求; 和项目的利益相关者协 商,在一般需求、核心需求、费用和可行性之间 权衡; 最终确定的需求要明确最终的软件系统必 须具有的特性和服务。这些需求被记录在 一 个称 为软件需求规格说明 (software requirements specification document ) 的文档中。从某种意义上讲,这个文档是所涉及的各方之间达成的书面协 7.2 软件生命周期 219 议,它的目的是指导软件开发,也为日后开发过程中可能产生的分歧提供了解决方法。像 IEEE 这 样的专业组织和美国国防部这样的大型软件客户都己经采用了软件需求规格说明文档编写的标 准,这样的事实已经证明软件需求规格说明文档十分重要。 从软件开发者的角度来看,软件需求规格说明文档应该能够为软件的开发顺利进行制定严 格的目标。然而,大多数情况下,需求文档很难提供这种稳定性。事实上,软件工程领域里的 大多数实践派都 认为: 在软件工程产业中,导致花费增加和延期交付软件产品的最主要原因是 不良沟通以及不断改变客户需求。举例来说,在地基已经建好的情况下,很少有客户会坚持对 褛盘的建设计划做大的修改。但是在许多组织机构进行扩编或变更的情况下,软件产品幵始构 建后,对软件系统的需求也还是会不断涌现(也就是说,软件的需求不会因为软件的构建而停 止)。 其原因可能是公司决定把原本仅为附属机构开发的软件系统推广到整个公司,或者是技术 的进步取代了初始需求分析阶段的可行性。软件工程师已经发现,在任何情况下,与项目的利 益相关者进行直接地、经常性地沟通都是必需的。 2. 设计 如果说需求分析阶段提供了对一个即将幵发的软件产品的描述,那么设计主要是为预期系 统的构建提出一个解决方案。从某种意义上讲,需求分析阶段指明要解决的问题,而设计阶段 则是制定问题的解决方案。从一个外行人的视角来看,需求分析阶段常常等同于决定软件系统 应该做些什么,而设计阶段则是决定系统怎样完成这些目标。虽然这种描述是有意义的,但很 多软件工程师认为它是有缺陷的,因为实际上在需求分析阶段有很多要考虑“如何实现”的情 况,在设计阶段也有很多要考虑“应该做些什么”的情况。 软件系统的内部结构在设计阶段建立。设计阶段的结果是可被转化为程序的软件系统结构 的详细描述。 如果项目是建造一座办公大楼,而不是构建一个软件系统,那么在设计阶段应该为大褛制 订详细的结构上的计划并力求满足指定需求。例如,这样的计划应该包含在各个细节层次上描 述所建大楼的蓝图汇总。正是源于这些文档,实际的大楼将被建造。制订这些计划的技术已经 经历多年的发展,包括标准的符号系统以及大量的建模和图形化方法学。 同样,在软件的设计中画图和建模也发挥着很大的作用。然而,软件工程师所用的方法学 和符号系统与建筑领域里所使用的相比,稳定性不太好。确实,与建筑学这个成熟的学科相比, 软件工程显得非常动态化,因为软件工程的研究人员一直在努力地寻找软件开发过程中更好的 办法。我们将在 7.3 节探究尚不稳定的软件工程方法,并在 7.5 节详细讨论当前的符号系统以及 与它们相关的图形化/建模方法学。 3. 实现 实现阶段涉及程序的具体编写、数据文件的创建和数据库的开发。在实现阶段,我们看到 了软件分析员 (software analyst , 有时候也称为系统分析员)和 程序员 ( programmer ) 之间的工 作的不同。软件分析员参与整个开发过程,他的工作重点可能在于需求分析与设计步骤,而程 序员的主要工作是实现这些步骤。最狭义地说,程序员负责写程序来实现软件分析员提出的设 计。做了这样的区分,我们还要注意的是,在计算机领域里并没有一个总的权威来控制术语的 使用。许多有着软件分析员头衔的人,本质上就是程序员,而许多有着程序员(也许是高级程 序员)头衔的人,从完全意义上讲是软件分析员。我们很快就可以看到,术语上的这种模糊是 因为今天的软件开发过程中的步骤经常会交叉重叠。 4. 测试 在过去传统的开发阶段中,测试本质上等同于调试程序和确认最终的软件产品是否与软件 220 第 7 章软件工程 需求规格说明文档相一致的过程。但是如今,这样的测试观念被认为太过狭隘。程序不是唯一 在软件开发过程中被测试的人工产品,实际上整个开发过程中的每个中间步骤的成果都必须进 行精确性测试。此外,我们将在 7.6 节中看到,现在测试被认为是为全面保证质量所作努力中的 一部分,这一目标渗透于整个软件生命周期。因此,很多软件工程师认为测试不应该再被看做 是软件开发过程中独立的一步,而是(许多的事例表明)应该纳入到其他步骤中,形成3步开发 过程,其中每一步都应该有自己的名称,如需求分析和确认、设计和验证以及实现和测试。 遗憾的是,虽然有现代的质量保障技术,大型的软件系统即使经过了严格的测试,还是可能包 含大量的错误,其中许多错误可能在软件的生命周期内都检测不出来。然而,另一些错误可能会造 成重大的故障。消除这种错误是软件工程的目标之一。事实上这些方法仍然流行意味着还有许多研 究要做。 问題与练习 1. 软件生命周期的开发阶段是如何影响维护阶段的? 2. 简要说明软件生命周期之开发阶段的4个步_ (需求分析、设计、实现和测试)。: ㉝ ||誠_述,软件需求_格说明文档的__ ㈤ … • - - H . . * i • • • / - —- , ■ - I - ---- 7.3 软件工程方法 软件工程早期的方法强调以一个严格的顺序,按照需求分析、设计、实现和测试分阶段进 行。理由是,在大型软件的开发过程中,允许做出随意变更会冒太大的风险。结果,软件工程 师 坚持: 在设计之前必须先完成整个系统的需求 分析; 同样,设计完成后再开始实现。结果产 生了现在称为瀑 布模型 (waterfall model ) 的一个软件开发过程,因为这种开发过程只会按照一 个方向进行。 近年来,由瀑布模型规定的高度结构化环境与“自由发挥”的“摸着石头过河”的开发过 程之间的矛盾带来了软件工程技术的变化,而后者通常对创造性的问题求解至关重要。软件开 发过程中出现的增 量模型 (incremental model ) 就说明了这一点。依据这个模型,所需的软件系 统以一种渐近的模式来构建,即软件产品先是以功能有限的简化版本出现,一旦这个版本的系 统通过测试或经未来用户的评估,更多的功能就以递增的方式加到系统中,然后再测试,直至 整个系统全部完成。例如,为医院开发的病人记录系统,一开始系统只需要能够查看整个记录 系统中的一小部分病人的记录样本就可以了,一旦这个版本的系统能够工作,其他功能(如增 加和更新记录的功能等)就可以以渐进的方式加入到系统中去。 另外一种与严格遵循瀑布模型不同的是迭 代模型 (iterative model )。 事实上,尽管它与增量 模型是不同的,但二者是非常相似(有时是相同)的。增量模型使用扩展产品的每个前期版本 到更大版本的概念,而迭代模型则使用改进每个版本的概念。实际上,增量模型通常会包含一 个基本的迭代过程,而迭代模型常常渐进增加特性。 一 个典型的迭代技术的例子是 National 软件公司创造的 RUP (Rational Unified Process , 统一 软件开发过程), RUP 与 “ cup ” 押韵,现在这家公司是 IBM 的一个分公司。 RUP 在本质上是一 种软件开发范型,它重新定义了软件生命周期中开发阶段的每一个步骤,并提供执行这些步骤 的指导。这些指导,连同支持它们的 CASE 工具,都被 IB 1 V [在市场上交易。 今天, RUP 在软件领 域被广泛地采用。事实上,它的流行促进了非专利版本- 统一过程 (unified process ) -的 发展,这在非商业基础上非常有用。 7.4 模块化 221 增量模型和迭代模型反映出软件幵发 采用原型开发 ( prototyping ) 这样一种趋势,也就是 把 预期系统先做成一个非完整版本,称 为原型 ( prototype ), 并加以评估。在增量模型中,将这 些原型发展为一个最终的完整系统的过程 称为演化式原型开发 ( evolutionaryprototyping )。 在迭 代性更强的情况中,原型可能会弃而不用,以使得最后设计有全新的实现,这种方法称为抛弃 式原型开发 (throwaway prototyping ) 。 快速原型开发 (rapid prototyping ) 通常属于抛弃式原型 开发这个范畴,这种方法中,幵发过程的早期就很快构建一个预期系统的简单原型。这个原型 可 能只由几个屏幕图像构成, 用 来演示系统将 如何与用户交 互以及系统将有哪些 功能。其目标 不是作出一个可运行版本的系统,而是作为一个示范工具,来理清软件开发过程中的各个部分 相互交流的关系。例如,快速原型有利于在需求分析阶段确定系统需求,也能帮助在销售期间 向潜在的客户进行推销介绍。 由计算机的热心者/爱好者使用多年的增量和迭代开发的一种变种方法,称为 开放源码开发 ( open-source development ) o 这是今天许多自由软件开发采用的 一 种方式。最著名的例子也许 就是 Linux 操作系统,该系统的开放源码开发工作最初是由 Linus Torvald 领导的。软件包的开 放源码开发遵循以下过程。先是单个作者开发一个初始版本的软件(通常是用于满足该作者自 己的需求),然后将其源代码和相关文档发放到因特网上,其他用户可以免费下载和使用这个软 件。由于这些“其他用户”拥有该软件的源代码和相关文档,他们就能修改或增强这个软件的 功能,以使之满足自己的需要,或者是改正他们发现的错误。接下来,他们就将这些改动报告 给原作者,原作者就将这些改动整合到自己发布的软件中,使软件的扩展版本可用于更进一步 的修改。实际上, 一 个星期内软件包就有可能经过几次的扩展升级。 ' 由瀑布模型转化而来的最显著的方法就是称为敏 捷方法 (agile method ) 的方法学集合,它 们都建议在增量基础上的早期和快速实现,响应需求变更,降低严格需求分析和设计的重要性。 敏捷方法的一个例子就是极 限编程 ( XP )。 根据 XP 模型,由少于12名成员组成一个软件开发团 队,他们在共同的工作场所自由地交换想法,在开发项目过程中相互协作,通过每天不断重复 非正式需求分析、设计、实现和测试这样一个周期开发过程,以增量的方式开发软件。这样, 软件包的新扩展版本定期出现,每个新版本都能由项目的利益相关者进行评估,并以此为基础 做进一步的增量。概括说来,敏捷方法具有灵活性的特点,这与瀑布模型完全相反。瀑布模型 的典型情况就是经理和程序员在各自的办公室工作,> 并严格地完成整个软件开发任务中明确定 义的那部分工作。 比较瀑布模型与 XP 模型中所描述的差异,揭示了软件工程方法学的广度。这些方法应用于 软件开发的过程,期待以一种有效的方式找到更好的方法来构建可靠的软件。这个领域的研究 仍在继续,虽然取得了一定的进展,但还有许多工作要做。 2. 铖出;3棘与 邀櫧 寒街糢虚不商的弁发范型! V ; :.-'' 3. 传统的酱化式原型弁复与牙放源码开方法之间的区别是什么? _ 4. 对于通过开放源码方法开发的软件的所有权而言,你 认为商 能会出现什么样的磨在向题 7.4 模块化 7.2 节中有一句关键性的 陈述: 要修改软件,就必须理解这个程序,或者至少是这个程序中 222 第 7 章软件工程 相关的那部分。即使是小程序,要想达到这样的理解也是相当困难的,而对于大型的软件系统, 如果没有模块化,那几乎是不可能的。模块化 ( modularity ), 就是把软件分割成多个易于处理 的单元,通常称为模块 ( module ), 每个模块仅仅承担整个软件的 一 部分功能。 7.4.1 模块式实现 模块可以以不同的方式实现。我们己经看到(第5章和第6章),在命令型范型的环境中, 模块表现为过程。与之对应的是,面向对象范型则是利用对象作为其基本的模块要素。这些差 别非常重要,因为它们决定了最初的软件设计过程中的潜在目标。这个目标是将全部工作表示 为个别的、易于管理的过程,还是确定系统中的对象并理解它们之间如何相互作用? 为了说明这一点,我们来考虑用命令型范型和面向对象范型是如何开发一个模拟网球比赛 的简单模块化程序的。在命令型范型中,我们首先考虑的是肯定会发生的动作。因为每场网球 比赛都是从一名选手发球开始,所以我们可以首先考虑构造名为 Serve 的过程(这是基于选手 的特性,也许是一个概率点),用来计算球的初始速度和方向。接下来,我们需要确定球的路径。 (是否将撞在网上?它将弹回到什么地方?)我们可以把这些计算放在另外一个名为 CoxaputePath 的过程中。下一步可能就要确定另外一名选手是否能击回这个球。如果能够击回 这个球,我们还必须计算球的新的速度和方向,可以把这些计算放在名为 Return 的过程中。 照这样继续,我们可以构造出如图 7-3 所示的 结构图 (structure chart ) 所描述的模块化结构 。在 这个图中,过程用矩形表示,过程之间的依赖关系(由过程调用来实现)用箭头表示。特别是,这个 图表明了整个比赛是由名为 ControlGame 的一个过程来控制的。为了完成工作, ControlGame 过 程又调用了 Serve 、 Return、ComputePatli 和 UpdateScore 这4个过程的服务。 注意,这个结构图中并没有描述每个过程如何完成自己的工作,确切地说,这个图仅仅是 确定了过程并描述了过程之间的依赖关系。事实上, ControlGame 过程要完成自己的工作会先 调用 Serve 过程,然后重复调用 ComputePath 过程和 Return 过程,直到有一名选手没有击中球 为止。最后, ControlGame 在调用 Serve 过程再重复以上整个过程之前,调用 UpdateScore 这 个过程的服务来更新比分。 至此,我们仅仅是获得了所需系统的一个框架,但思路已经建立起来了。按照命令型范型, 通过构思系统必须实现的功能,我们已经完成了程序的设计并得到了设计方案,其中的模块就 是过程。 现在,我们重新考虑这个程序的设计,而这次是在面向对象范型的环境中考虑的。我们开 始的想法就是用两个对象来表示两位选手,即 PlayerA 和 PlayerB 。 这些对象将有同样的功能 和不同的属性。(两名选手应该都能发球和回击球,但是其技巧和力度不同。)因此,这些对象 就是同一个类的实例。注意,我们在第6章介绍了类的概念,类是定义与每个对象相关联的过程 (即方法)和属性(即实例变量)的模板。我们把这个类称为 PlayerClass , 该类将包含 Serve 方法和 Return 方法,用来模拟选手的相应动作。这个类中还将包括选手的内部属性(如 skill 7.4 模块化 223 和 endurance 等),这些属性的值反映了选手的特征。到目前为止,可以用图 7-4 来表示我们的 设计结果。从图中可以看出, PlayerA 和 PlayerB 是 PlayerClass 类的两个实例,而这个类包 含了 Skill 属性和 Endurance 属性,同时也包含了 serve 方法和 returnVolley 方法。(注意, 在图 7-4 中我们己经用下划线标注出对象的名称,以此来区分它们与类的名称。) 接下来,我们需要一个对象来实现裁判的功能,帮助判定选手完成的动作是否合乎规则。 例如,发球是否过网?球是否落在了球场的合适位置内?为此,我们可以建立一个名为 Judge 的对象,该对象包含 evaluateServe 方法和 evaluateReturn 方法。如果 Judge 对象判定发球 或回球合乎规则,那么比赛 继续; 否则, Judge 对象会给一个名为 Score 的对象发消息,告之 它记录下相应的结果。 类 类名—— 1 '域1%«臟恭 嘉赫 ㈤ rm ' i . — : M ■- 丄爲_"瘍;::泛 4 l-- 1 ■,二‘一 - - f- - 乂, - l-T- - Z~ - 属性- —— ii ®. 方法- — 疒 U_ : A- J 二 r _ - , - , =?*-—-^,v" ■ "V j :,,: - 备 j-;= -_r n ■,‘ - ■ L - B — .. ^ : j . •> • — •• ■ — — 对象 图7~4 PlayerClass 类的结构和它的实例 此时,网球程序的设计包括 4 个 对象: PlayerA 、 PlayerB 、 Judge 和 Score 。 为了说明我 们的设计,考虑在网球比赛中可能发生的事件序列(如图 7-5 所示,图中对象以方框的形式来表 示)。这个图是要把对象之间的通信表示成调用对象 PlayerA 中的 serve 方法的结果。当我们从 上向下依次看图时,会发现事件是按次序发生的。就如同第一个水平箭头所表示的那样, PlayerA 通过调用 evaluateServe 方法向对象 Judge 报告它的发球,然后对象 Judge 判定发球 是否有效,并且通过调用 PlayerB 的 returnVolley 方法请求 PlayerB 回球。当 Judge 判定 PlayerA 产生错误,并且请求 Score 对象记录下结果时,网球比赛结束。 ::麵國 ^re 图 7-5 由 PlayerA 的 Serve 导致的对象间的交互 和命令型范型例子的情况一样,面向对象程序现阶段也是非常简单的。然而,我们已经取 得了很大的进步,能够很清楚地理解面向对象模式下是如何进行模块化设计的,而在此模块化 设计中,其基本的构件就是对象。 224 第 7 章软件工程 7 -4.2 絹合 通过前面的介绍我们知道,模块化是开发出易于管理的软件的一条途径。其基本思想是, 以后的任何修改可能只会涉及少数几个模块,允许个人对系统的修改只集中在系统的有关部分, 而不是整个系统。当然,这里有个前提,就是对一个模块的修改不会无意中影响到系统的其他 模块。因此,当设计一个模块化系统的时候,其目标就应该是做到模块之间的最大独立性,或 者换句话说,就是使模块之间的联系尽可能少。这种联系称为模块之间的耦合 ( cmapling )。 事 实上,用来衡量软件系统复杂度(并且这样就获得了一种估算维护软件系统的所需开销的方法) 的一个指标就是度量该系统的模块间的耦合。 模块间的耦合有多种形式。 一 种是控制耦合 (control coupling ), 出现在一个模块移交执行 控制给另外一个模块时,如过程调用的情况。图 7-3 里的结构图就表示了存在于过程之间的控 制賴合。具体来说,从 ControlGame 模块到 Serve 模块之间的箭头说明了前者将控制权传递 给后者。图 7-5 中的结构图也代表了一个控制耦合的情况,图中的箭头所描绘的路径就代表了 控制权在对象之间的传递。 模块间的另一种形式的耦合是数据耦合 (data coupling ), 这是指模块间的数据共享。如果 两个模块是通过共享同一个数据项相互作用的,那么当对一个模块进行修改时,可能会影响到 另外一个模块,并且对数据本身格式的修改在这两个模块中都会有反映。 过程间的数据耦合有两种形式。 一 种是以参数的形式从一个过程到另一个过程进行显式的 数据传送。这种耦合在结构图中是这样表示的,即用过程之间的箭头指示数据的传送。箭头的 方向表明在此方向上进行数据项的传送。例如,图 7-6 是图 7-3 的扩展版本,在此图中,我们可 以看出当 ControlGame 过程调用 Serve 过程时, ControlGame 过程会将需要模拟的那位选手的 属性告知给 Serve 过程。当 Serve 过程完成后,它就将球的轨迹报告给 ControlGame 过程。 图 7-6 包含数据耦合的一个结构图 类似的数据耦合也发生在面向对象设计中的对象之间。例如,当 PlayerA 对象请求 Judge 对象对其发球进行判定时(见图7-5),它必须将球的轨迹信息传递给 Judge 对象。另一方面, 面向对象设计模式的一个优势就在于它从本质上倾向于将对象之间的数据耦合减小到最低。这 是因为对象的方法易于包括操作对象内部数据的所有过程。例如, PlayerA 对象将包括该对象 的有关属性信息和针对这些信息的处理方法。因此,没有必要将这些信息传递给另外一些对象, 这样对象之间的数据耦合就能达到最小。 与通过参数进行显式的数据传递方式不同的是,数据可以以全局数据 (global data ) 的形式 在模块之间进行隐式共享。全局数据是可以自动地被整个系统中的所有模块使用的数据项。这 与局部数据项不同,局部数据项只能在某个特定的模块中使用,除非明确地传递给了另外一个 模块。大多数高级语言提供了全局数据和局部数据的实现方法,但是对全局数据的使用应当谨 慎。全局数据使用的问题在于,如果某个人试图修改依赖于全局数据的一个模块,那么他就很 难确定正被修改的模块与其他模块之间有怎样的相互关系。简而言之,全局数据的使用降低了 模块作为一种抽象工具的使用价值。 7.4 模块化 225 7.4.3 内聚 正如模块间的耦合应最小化,同样重要的是,每个模块的内部绑定程度应该最大化。术语 内聚 ( cohesion ) 就用来表示这种内部绑定,或者说模块内部各部分的关联程度。为充分理解内 聚的重要性,必须考察系统的最初开发并考虑这个软件的整个生命周期。如果有必要在模块,中 作出修改,那么存在于模块中的各种不同的活动会搅乱原本简单的一个过程。所以,软件设计 人员在寻求模块间的低耦合的同时,还力求做到模块内部的高内聚。 一种内聚度较弱的内聚形式称为逻 辑内聚 (logical cohesion ) o 模块内的逻辑内聚是由其内 部元素本质上实现逻辑上相似的活动所引起的。例如,考虑一个模块,它完成整个系统与外界 进行通信的功能。粘合这个模块的“胶水”是模块中的所有活动都用以处理通信。然而,通信 的主题各不相同,有的可能是用来获取数据,有的可能是用来报告结果。 一种内聚度较强的内聚形式 称为功能内聚 (functional cohesion )。 这就表示模块中所有部分 都集中于实现某一项功能。在命令型范型的设计中,如果把模块的子任务独立在其他模块中, 并将这些模块用作抽象工具,那么该模块的功能内聚的程度通常会增强。这一点在模拟网球比 赛的例子中得到了很好的说明(参见图7-3)。在该图中 ControlGame 模块将其他模块用作抽象 工具,以便它能集中调度整个比赛,而不是把精力分散在实现发球、回击球和维护比分这样的 细节上。 在面向对象设计中,因为对象中的方法常常执行松散相关的活动,其唯一的共同“纽带” 就是它们都是由同一个对象执行的活动,所以全部的对象通常在逻辑上内聚。例如,在模拟网 球比赛的例子中,每个选手对象都包含发球和回击球的方法,这些方法是明显不同的活动,所 以这样一个对象仅仅是在逻辑上内聚的模块。然而,软件设计人员应当力求做到使一个对象中 的每个方法都在功能上内聚。也就是说,即使对象在整体上仅仅是逻辑上内聚,对象里的每个 方法也应当只实现一个功能内聚的任务(参见图7-7)。 图 7-7 —个对象的逻辑内聚和功能内聚 7.4.4 信息隐藏 信息隐藏 (information hiding ) 是好的模块化设计的一个基本特征,它指的是限制软件系统 的指定部分的信息。这里的术语信息应该从广义阐释,它包括关于程序单元结构和内容的任何 知识。因此,它包括数据、用到的数据结构类型、编码系统、模块的内部组成结构、过程单元 的逻辑结构和任何涉及模块内部特性的因素。 226 第 7 章软件工程 信息隐藏的关键就是阻止模块的动作对其他模块产生不必要的依赖或影响。否则,就有可 能导致模块产生错误,这些错误可能是在其他模块的开发中带来的,亦或是在软件维护期间不 正确的维护带来的。例如,如果一个模块不限制其他模块对其内部数据的使用,那么这些数据 可能就会被其他模块破坏。或者,如果设计一个模块以利用另一个模块的内部结构,如果其内 部结构被修改了,随后它将可能产生错误。 要注意到信息隐藏具有两个化身,这是非常重要的。一个是作为设计目标的,另一个是作 为实现目标的。应当这样设计一个模块,使其他模块不需要读取它的内部信息,并且应当以强 化模块边界的方式实现一个模块。前者的例子是最大化内聚和最小化耦合。后者的例子涉及使 用局部变量、应用封装和使用完善定义的控制结构。 最后,我们应该注意到信息隐藏对于抽象主题和抽象工具的使用极为重要。实际上,抽象 工具的概念是“黑盒”的概念,用户可以忽略它的内部特性,这样就允许用户集中考虑手头更 大的应用。在这种情况下,信息隐藏相当于封装抽象工具的概念,就像安全罩可以用来保护复 杂的、具有潜在风险的电子设备一样。保护他们的用户远离内部危险,同样也保护内部,以防 来自其他用户的侵扰。 7.4.5 构件 我们已经提到,软件工程领域里的一个障碍就是缺乏预制的现成构件块来构建大型的软件 系统。在这一点上,软件开发中的模块化方法让我们看到了希望。特别是,面向对象程序设计 范型显得尤其有用。这是因为对象,它们来自完备的、自我包含的单元,这些单元明确定义了 与其外部环境的接口。一旦对象(更准备地说,是一个类)设计成能完成某种特定功能时,它 就可以在任何要求提供这种服务的程序中用来实现这个功能。此外,继承提供了一种对预制对 象的定义进行改进的方法 ( 在对象定义必须定制以符合一个特定应用的需要时)。 于是,面向对象编程语言 C #、 Java 以及 C # 都伴随有一组预制的“模板”这一点就不足为 奇了。通过这些模板,程序员可以很方便地实现对象并用来完成特定功能。具体来说, C ++ 拥 有 C ++ 标准模板库, Java 编程环境伴随有 Java 应用编程接口 ( API ), C # 程序员可以访问 . NET 框 架类库。 对象和类有可能为软件设计提供预制的构建块,但是它们还不太完美。一个问题是它们提 供相对较小的模块来构建系统,所以对象实际上是更通用的构件 ( component ) 概念中的一个特 例,构件就是软件的一个可复用单元。实际上,大多数构件都是基于面向对象范型的,并且表 现为一个或多个对象组成的集合的形式,其功能是作为一个自包含单元。 构件的开发和利用的研究导致了 称为构件架构 (component architecture , 也就是通常所说的 基于构件的软件工程)的领域的岀现。在此领域中,传统的程序员 被构件装配员 (component assembler ) 所代替,由构件装配员把预制的构件装配成软件系统。在许多开发环境中,常常用 图形界面中的图标来表示预制的构件。构件装配员并不涉及构件内部的编程,而是在预先定义 好的构件集合中选择相关的构件,然后将它们进行最小化的定制并连接,从而获得所需要的功 能。确实, 一 个设计好的构件的属性就是不需要经过内部的修改就可以进行扩展,来包含一些 针对特定应用的特性。 构件架构在智能手机系统这一领域中尚无用武之地。因为这些设备的资源有限,数个应用 实际上只是一组相互协作的构件的集合,每个构件会谨慎地为其应用提供某个功能。例如 ,一 个应用中的每个显示屏通常是一个独立的构件。在其背后,可能存在其他服务构件,或用于存 储和访问存储卡上的信息,或执行某个持续的功能(如播放音乐),或用于通过因特网访问信息。 7.5 行业工具 227 每一个这样的构件都按需独立启动和终止,这样才能高效地服务于用户。但是,应用本身看起 来却是显示和动作的 一 个无缝系列。 : 柄峰..麵藏间辱。窥瓣•■雜:果辦司, 为竦开发:安_^& 部峰 以满定据需要。'作絲藏#一知 XYZ ^ 司〗又建灰了 〒套 fC 机詾铬系.,洚炅龙 用来# _个舍公•系统。这样,每个员 工的办 公桌玉都赛一蠢_4 : :很锋这些? pfc 不仅能用秦谛问新的:数振管璉:系鍊,|; ^ 为可定制的车其,-铱员:主期秦奉據高自:已的其作产:出。例如;^:某名员工可以开发^个电子制 表 # 序来枝 , 璋样一 赉定麟的>^可錐_舞茶寒善$或凑没^过 ■■顧祕备禱翁^^完!#鏵錶士蜋的讀命,, 这些, 升起、養 參开槔 家秦司「而_这备程序的其他茼事却并不懂这些 的系统变藏了二令接钟義、馬文相的、出镨频 ■ liip : ^i|l!|||ip^1l : ! ; iMil;si ; !^!lf!ly|i : 顔 ; ; i ::ii : ;i.;[f;i i . ; g : | 轉 ::::: : ::.f;::I:; : : : ■ :: ; : ; ( ; ^1;^;^; : : ,-■! ; ;: 3..:: : ::. :| :' .... . f : : ': s S:S :: '■ : ;p:.;:: 1 ' 11 ., ._ —— “ ::: : 二 ■ :__••■ . _ I p ■' p r cci i.ci I .1 _.". I -.1 : •_•+ I I - ( I 1 1 _- I • : |;i '_•+_•_•_•_• ••,-,,, -- -- ,*• - - ^ _•_-___ - - .. v . -- ■ ■ •-• ■ -.- .:**.. 二 .__ I : ( . !:• i -' :i:i i:i| :i: I : I :::::.: _.. 編 * fo 棒^«_成亇几个商去 ,网 錄比_夯衝 r 凡扃,试 :餘綱緻'■禪禱泰:乂_赛内 赛麵雞 i 座爵: :4乂荐$^, : _^夢_弋'^“'::_-?:- : 乂: : ' :: 兔梅___被■兔效蠢拟孕的藥-袭# 7.: 传繞:与构錐装観為去 | 司有存么_别?::_ 二 丸:擴撰^数樣碧个人_应用 C 滅讶历 、蔴系人、-钟、:社会 也网氣 电子邮件系统; 7.5 行业工具 本节里,我们研究一些在软件开发的分析与设计阶段使用的建模技术和符号系统。其中一 些技术和符号系统是在软件工程学科中以命令型范型为主导的年代里开发的。现在,在面向对 象范型环境中也可以找到它们中某些的身影,而另外的一些如结构图(见图 7-3) 则是专门用于 命令型范型的。我们首先考虑一些从命令型范型发展而来的技术,然后研究较新的面向对象的 工具和设计模式的扩展功能。 7.5.1 较老的工具 尽管命令型范型致力于依据过程来构建软件,但确定这些过程的方法是考虑将被操作的数 据,而不是过程本身。其思路是,研究数据在系统中如何流动,就能确定在哪儿修改数据格式, 或者在哪儿对数据的路径进行合并或拆分。因此,就确定了进行处理的位置,这样一来,通过 228 第 7 章软仵工程 _____ 数据流的分析就能确定过程。 数据流图 (dataflow diagram ) 是表示从数据流分析过程中所获得 的信息的一种方法。在数据流图中,箭头表示数据路径,椭圆表示数据操控发生的地点,矩形 表示数据源和数据存储。作为一个示例,图 7-8 表示的是医院账单系统的一个基本的数据流图。 注意,该图表明 Payments (从病人中“流出”的)和 PatientRecords (从医院文件中“流出”) 在桐圆 Process Payment s 处合并,并从此处将 UpdatedRecords “流回”到医院文件 。 病人记录 图 7-8 —个简单的数据流图 数据流图不仅能在软件开发的设计阶段帮助确定过程,还能在分析阶段帮助理解预期系统。 实际上,构建数据流图可以作为一种用来改善客户与软件工程师之间的交流的方法(因为软件 工程师一直为理解客户需要什么而努力并且客户努力描述个人愿望),所以,即使在命令型范型 已经不太流行的情况下,这些数据流图还有其应用价值。 软件工程师已经用了很多年的另一种工具就是数 据字典 (data dictionary ), 它是关于整个软 件系统中出现的数据项的一个中央信息库。这些信息 包括: 为引用每个数据项所采用的标识符、 每个数据项里的有效条目的构成情况(数据项一直是数字型的,还是一直是字符型的?分配给 该数据项的值的可能范围是什么)、数据项存放在什么地方(数据项是要存放在文件中或数据库 中吗?如果是这样的,具体存放在哪一个里面 X 软件在什么地方会引用这些数据项(哪些模块 需要数据项的信息)。 构建数据字典的一个目标是,増强软件系统的潜在利益相关者与软件工程师之间的沟通, 其中软件工程师负责将利益相关者的需求转化为需求规格说明文档。在这样的环境下,构建数 据字典有助于确保这样一个事实,即如果部分数字不是真正的数字型的,那么在软件的分析阶 段就可以发现,而不用等到在后面的设计和实现阶段才发现这个问题。构建数据字典的另一目 标是确立整个系统的一致性。借助构建字典常常可发现冗余和矛盾。例如,一个数据项在库存 记录中称为 PartNumber , 而在销售记录中可能就改称为 Partld . 还有,在人事部门可能会 用 Name 这个术语来表示一名员工,而在库存记录中可能用来表示一个零件。 7-5.2 统一建模语言 数据流图以及数据字典是在面向对象范型出现以前,软件工程领域发展比较成熟的一些工 具。即使是在命令型范型(它们最初是针对命令型范型开发的)现在已经不太流行的情况下, 这些工具还是能继续找到其应用价值。现在,我们转而研究更为先进的工具集,称为 UML (Unified Modeling Language , 统一建模语言)。统一建模语言是基于面向对象范型思想发展而来的。然而, 在这个工具集中,我们讨论的第一个工具是 用例图 (use case diagram )。 无论其潜在的范型如何, 这个工具都是非常有用的,因为它仅仅尝试着从用户的视角来捕捉预期系统的画面。图 7-9 表示 的就是用例图的一个例子。 7.5 行业工具 229 用例图是用大的矩形框来描述预期的系统,在这个矩形框中,系统与其用户之间的交互—— 称 为用例 (use case ) ^是用椭圆来表示的,而系统中的用户—— 称为参与者 ( actor ) ——用 火柴人表示(即使角色可能不是一个人,也这么表示)。这样,图 7-9 所表示的就是 Hospital Records System , 该系统在获得 Physician 或 Nurse 的请求时,就会完成 Retrieve Medical Records 这个用例 D 鉴于用例图是从预期系统的外部来观察系统的,所以 UML 提供了许多种工具,用于表示系 统内部的面向对象设计。其中的一种工具 是类图 ( class diagram ) ,它是一个标记系统,用来表 示类的结构和类之间的联系——在 UML 的术语中称 为关联 ( association )。 举一个例子,考虑医 生、病人和病房之间的关系,我们假定表示这些实体的对象是分别从类 Physician、Patient 和 Room 构造出来的。 图 7-10 表明了 Physician 类、 Patient 类以及 Room 类之间的联系在 UML 类图中是如何表示 的。用矩形框表示类,用线来表示关联,关联线上可能有标号,也可能没有。如果有,那粗体 箭头就可用来指明标号被读的方向。例如,在图 7-10 中带标记 cares for 的箭头指示医生医治 病人,而不是病人医治医生。有时关联线上带有两个术语标记,可以从任一方向读取关联。图 7-10 中的类 Patient 和 Room 之间的关联就印证了这一点。 cares for ( 照 _ ) 图 7-10 occupies ( 分配到) 0,1 Rbom hosts ( 容纳) •个简单的类图 除了指明类之间的关联之外,类图还能表达这些关联的多样性。也就是说,它能指明一个 类的多少个实例与另一个类的实例相关联。这个信息被记录在关联线的两端。图 7-10 指明每位 230 第 7 章软件工程 病人可以占据一个房间,而每个房间能供0位或1位病人住宿。(我们假定每个房间都是私人房 间。) * 表示一个任意的非负数。这样,图 7-10 中的 * 表示每位医生可以医治多位病人,而在关联 的医生端的1表示每位病人只被一位医生医治。(我们的设计只考虑主治医生的作用。) 为了完整性起见,我们应该注意到关联的多样性有3种基本形式:一对一联系、一对多联系 和多对多联系,如图 7-11 所示。 一对一联系 ( one - to-one relationship ) 的一个例子就是病人和私 人病房之间的关系,其中每位病人只能分配到一个房间,而且每个病房只分配给一位病人 。一 对多联系 ( one - to - manyrelationship ) 的一个例子就是医生和病人之间的关系,其中每位医生可 以照顾多位病人,而每位病人只有一位(主治的)医生照顾。在这个例子中,当我们考虑将病 人与咨询医生之间的联系加入病人与医生之间的联系时,就形成 了多对多联系 ( many - to-many relationship ), 即每位病人可以有多位咨询医生来辅助治疗,而每位咨询医生可以帮助多个病人。 —对一 •aftMMIIilllMaMIHIilMilliBIMIIIiOIMBaniiliiniMliMHIlHnPiW x 类型 Y 类型 的实体 的实体 m -驪 Mutt am m - m ac mm Si - Si m SKt ~ lUS —对多 X 类型 Y 类型 的实体 的实体 多对多 X 类型 Y 类型 的实体 的实体 图7_1〖 X 类型实体与 Y 类型实体间的一对一、一对多以及多对多联系 在面向对象的设计中,经常会出现一个类表示另一个类的更加具体的版本。在这种情况下,我 们说后者是前者的泛化。 UML 提供了特殊的符号来表示泛化。图 7-12 给出了一个例子,它描述了类 MedicalRecord 、 SurgicalRecord^POf f iceVisitRecord 间的泛化。类间的关联用带空箭头的 箭头表示,这是 UML 表示泛化的关联符号。注意,每一个类都是由一个矩形表示,里面包含了类的 名称、属性和方法(格式参见图74)。这是 UML 在类图中表示类的内在特征的方法。图 7-12 中描述 的信息是: MedicalRecord 类是 SurgicalRecord 类 的泛化,同时也是 OfficeVisitRecord 类的泛化。也 就是说, SurgicalRecord 类和 Of ficeVisitRecord 类包含了 MedicalRecord 类的所有特征,并附加了那 些明确地列在它们矩形框中的特征。因此, SurgicalRecord 类和 Of ficeVi si tRecord 类都包 含病人的姓名、医生的姓名和病历日期,但 SurgicalRecord 类还包含手术流程、医院、出院日 期和准许病人出院的权力,而 OfficeVisitRecord 类包含了症状和诊断。这3个类都有打印医疗记录的功 能。 Surgi calRecord 类和 Of f iceVisitRecord 类中 的 PrintRecord 方法是 MedicalRecord 类中 PrintRecord 方法的特殊化,它们都可以打印其类特 有的信息。 图 7-12 描述泛化的一个类图 7.5 行 业工具 231 回顾第6章 (6.5 节),在面向对象编程环境中实现泛化的一个很自然的方式就是利用继承。 然而,许多软件工程师都告诫说,继承并不是对所有的泛化情况都适合。原因在于,继承导致 了类间的强耦合度,这种耦合在软件生命周期的后期并不希望出现。例如,类的改变会自动地 在它的所有继承类中得到反映,因此在软件维护阶段看起来很小的改动就能够导致不可预见的 后果。作为一个例子,我们可以假设一个公司为其员工开放一个娱乐设施,这也就意味着娱乐 设施里的所有具有成员资格的人员都是该公司的员工。为了给这个设施做一个成员表,程序员 可以利用继承依据早先已经定义的 Employee 类构建一个 RecreationMember 类。但是,如果 随着公司后来的效益提高,公司决定对员工的家属和退休员工也开放娱乐设施,于是 , Employee 类和 RecreationMember 类之间内含的耦合性问题将会变得更为严重。所以,使用继承的时候不 应当只考虑其方便性,而应当将继承的使用严格限制在需要实现的泛化一直不会更改的情况下。 类图代表的是程序设计中的静态特征,它不能表示程序在执行过程中发生的事件序列。为 了表示这种动态特征, UML 提供了一系列图的类型,它们统称为交互图 (interaction diagram )。 交互图的一种是序列图 ( sequence diagram ), 它描述了完成任务所涉及的个体(如参与者、完 整的软件构件或个体对象等)间的通信。这些图与图 7-5 类似,因为它们都用带有向下延伸的虚 线的矩形表示个体。每个矩形连同它的虚线称为生命线 ( lifeline ). 个体间的通信用连接合适生 命线的带标记的箭头表示,这里的标记指示被请求的动作。当自顶向下阅读图时,这些箭头是 按时间先后次序出现的。当个体完成请求的任务,并把控制返回给发出请求的个体(就像传统 的从一个过程返回)时,这时用一个指回原始生命线的无标记箭头表示通信。 因此,图 7-5 从本质上讲是一个序列图。但是,图 7-5 的语法本身有几个缺点。一个就是它 不允许获取两对手间的对称,我们必须画出单独的图来表示开始于 PlayerB 发球的网球,即使 交互的序列与 PlayerA 发球的非常相似。此外,图 7-5 只描述了一次具体的击球,一次一般的击 球活动肯定可以无限延展。形式化序列图有在单个图中获取这些变化的技术,虽然我们不需要 仔细研究这些,但还是应该简要地看一下图 7-13 中显示的形式化序列图,它描述了基于我们的 网球比赛设计的一个一般的击球活动。 締 ㈣ I evaluateServe ■T 他述臟 A 纖鷀_|纖 . __ 指定交互 段类型 [valid PI ay == true] ' 1 1 W 础、、 ! [fromServer == true] - 1 f return Volley | \4 -- 1 T 1 1 1 evaluateReturn ! - ► 1 1 . r i / 1 f ■ / [fromServer == false] retu rnVolley 1 1 1 ! evaluateReturn - 1 1 1 J, 1 1 - Pi i 1 - 1 -1 1 updateScore 指定控制 交互段的条件 图 7-13 描述一般网球的序列图 232 第 7 章软件工程 还要注意图 7-13 说明了整个序列图是包含在一个矩形—— 帧 ( frame ) -中的。帧的左上 角是一个包含了跟有标识符的字符 sd (意思是 “sequence diagram ”) 的五角形,这个标识符可能 是标记整体序列的名字,或(正如图 7-13 中的)是被调用来初始化序列的方法的名字。注意, 与图 7-5 对比,图 7-13 中表示参赛手的矩形并没有指定具体的参赛手,而仅仅指示它们代表 PlayerClass “ 类型”的对象。其中一个被指定为 self , 章思是这是一个其 serve 方法被激活去 初始化序列的对象。 关于图 7-13 的其他关键点是它处理两个内部的矩形,即交互段 ( interactionfragment ), 它们 用来表示一个图中的候选序列。图 7-13 包含了两个交互段,一个标记为 “ loop ”, 另一个标记为 “ alt ”。 这本质上是我们首次在 5.2 节伪代码中遇到的 while 和 if-then-else 结构。 “ loop ” 交互 段表明边界内的事件将重复,只要 Judge 对象判定 validPlay 的值 为真; “ alt ” 交互段表明根据 fromServer 的值是真是假,其中一个候选序列被执行。 最后,在这里介绍 CRC 卡 ( Class - Responsibility-Collaboration card , 类-职责-协作卡)的功 能还是比较合适的,尽管这部分内容不属于 UML , 但它在验证面向对象设计的有效性方面起着 很重要的作用。 CRC 卡是一张简单的卡片(如索引卡片),上面写着有关对象的描述。利用这种 方法,软件设计师为预期系统的每个对象做一张卡片,然后在模拟系统中用这些卡片来表示对象, 可以在桌面上进行,也可以通过一个“舞台表演”的实验,在实验中,设计团队的每个成员手持 一张卡片,然后扮演卡片上所描述对象的角色。这样的模拟通常称为结构化走查 (structured walkthrough ), 在设计阶段的找错能力要优于设计的实现阶段,因而被证明在设计阶段是一种比 较有效的方法。 7.5.3 设计模式 对软件工程师而言,越来越有用的工具是不断发展的设计模式集。设计模式 (design pattern ) 是用来解决软件设计过程中反复出现的问题的一种预先开发的方法。例如,适配器 ( Adapter ) 模式提供了一个解决办法,用来解决通过预制模块来构建软件的过程中经常出现的问题。具体 来说,预制模块可能已经具备了解决手边问题的功能,但可能还没有与当前应用相一致的接口。 在这样一种情况下,适配器模式可以用作一种标准方法,将模块封装在另外一个模块里,仅仅需 要为原始模块的接口与外部世界提供解释功能,这样一来,就允许原始的预制模块用于该应用中。 另一种成熟的设计模式是装饰者 ( Decorator ) 模式,它提供了一种用来设计系统的方法, 而所设计的系统依据当时的环境完成一些相同的活动的不同组合。这种系统会产生大量的选择, 如果没有经过仔细的设计,很可能导致软件极大的复杂度。但是,装饰者模式提供了一个实现 这类系统的标准化方式,从而产生了一种易于管理的解决办法。 , w , -- 对系统完善设计的严袼要求可以通过一个例证得到说明,这就是 Th ^ rac -25 ( 20世纪80 年代中期,医学界使用的一台基于计算机技术的电子加速放射治疗仪)所遇到的问题。该 参器的藏计缺陪导敢了 6例放射过量的事件发生、:其中3例导皴人员的死芒 a 後计的躺:陷包 括: (1) 机器界面设计的不合理,使得操作员在机器的放射量调整到合适值之前就能进行放 射_, (2) 硬什与软梦的舞神之闻的:协作性巷,韓果就导致了某曲棄全.角约 在一些更近的例子中,有因设计不当导致大面积停电、电话服务中断、金^融业务的重大 错 i 吴、空间探测器的失踪以及因特网的瘫痪等^如果你想对这个问题了解更多,请查輯风险 论坛(它的网 a ;^ http :// catless . ncLaauk / Risks )。 7.6 质量保证 233 设计模式中的重复问题的识别以及设计模式的创建和分类在软件工程领域里是一个不断进 步的过程。然而,其目标不仅仅是找到设计问题的解决办法,还要找到髙质量的解决方案,这 种解决方案在软件生命周期的后期能提供很好的灵活性。因此,对诸如耦合最小化和内聚最大 化这样好的设计原则的考虑,在设计模式的发展过程中起着重要的作用。 设计模式在发展过程中所取得的进展成果,在今天的软件开发包所提供的工具库中得到了 体现,如 Oracle 公司提供的 Java 编程环境以及微软公司提供的 . NET 框架等。事实上,在这些 “工具包”中找到的大多数“模板”本质上是设计模式的框架,这就为设计问题找到了现成的、 高质量的解决方案。 最后我们要提到的是,软件工程里的设计模式的出现是不同的领域相互促进的一个很好的 例子。设计模式的起源来自于 Christopher Alexander 在传统建筑领域里的研究,他的目标是发现 那些提高建筑设计质量的特征,然后开发包含这些特征的设计模式。今天,软件设计中已经包 含了他的许多思想,并且许多软件工程师仍继续从他所做的工作中汲取灵感。 问题与_习 I 1.1 只: ... a'i iW :;., 味平: .1 .气.屯, I :4::. 二 V .. C : 卜…:: 7.8 人机界面 回顾一下 7.2 节,其中讲到需求分析阶段的一项任务是定义要开发的软件系统将如何与它的 环境进行交互。本节我们将考虑与这个交互相关的主题,那就是当它涉及与人交流时的情况, 这是一个意义深远的主题。毕竟,应该允许用户把软件系统当做一个抽象工具来使用。这个工 具应该易于使用,最小化(理想情况下消灭)用户与系统间的交流错误。这意味着系统界面的 设计应方便用户的使用,而不仅是作为软件系统的权宜之计。 良好的界面设计非常重要,因为与系统的其他特性相比,系统界面更容易给用户留下深 刻的印象。毕竟,用户往往会从系统的 n 」 用性角度来审视一个系统,而不是从它如何巧妙地 执行其内部任务这个角度。从用户的视角来说,他们可能会根据系统界面在具有竞争性的系 统之间作出选择。因此,系统界面的设计可能成为判定一个软件工程项目是否成功的最终决 定因素。 由于这些原因,人机界面在软件开发项目的需求分析阶段已经成为一个很重要的关注点, 并是软件工程的一个不断成长的子领域。事实上,有些人认为人机界面的研究是一个完全独立 的领域。 智能手机界面便得益于这一领域的研究。为了能够实现口袋大小的袖珍设备,传统人机界 7.8 人机界面 237 面的元素(标准尺寸的键盘、鼠标、滚动条、菜单)已被新的方式所取代。(新的方式包括在触 摸屏上操作的手势、语音命令、具有高级的单词和短语自动输入功能的虚拟键盘。)尽管这标志 了重大的进展,但大多数智能手机用户认为进一步创新的空间还很大 对人机界面设计的研究主要来自于称 为人体工程学 ( ergonomic ) 和 知行学 ( cognetic ) 的 工程领域,人体工程学处理协调人类体能的设计系统,知行学处理协调人类精神能力的设计系 统。这两个学科中,人体工程学更好理解一些,主要是因为人类己经跟机器打了几个世纪的交 道。这些例 子有: 古代工具、武器和运输系统。这些历史大部分是不证自明的,但是有时人体 工程学的应用与直觉是相反的。一个经常被提到的例子就是打字机键盘(现在已经衍生为电脑 键盘)的设计,其中键被有意排列,以降低打字员的速度,这样早期机器上使用的分层机械系 统就不会卡住。 相反,与机器的精神交互是一个相对较新的现象,因此知行学在富有成效的研究和洞察力 启发方面拥有更高的潜力。通常这些研究成果更具有精妙之处。比如,从表面上看人类的良好 习惯有助于提高效率,但有些习惯也会导致一些错误,即使界面设计本意上是要解决问题的。 考虑一下用户要求操作系统删除一个文件的过程,为了防止误删,大部分界面都会要求用户确 认一个请求,这可能会通过一个类似“你是否真的想删除这个文件”的信息要求确认。乍一看, 这个确认信息好像解决了误删的问题,但是使用了这个系统一段时间后,用户会养成习惯,自 动回答“是”。这样,这个删除文件的任务就从包含删除命令和对问题思考后的响应的两步过程, 变成了 “删除一是”的一步处理过程,这就意味着当用户意识到提交了错误的删除要求时,这 个请求其实己经被确认,文件也己经被删除。 当人们需要使用几个应用软件包时,习惯的形成也可能会带来问题。这些软件包的界面可 能相似,但还是有些不同的。相似的用户操作可能会导致不同的系统响应,或类似的系统响应 可能需要不同的用户操作。在这种情况下,在某种应用软件上养成的操作习惯可能会在其他应 用软件上导致错误的发生。 另外一个与人机界面设计研究有关的人类特质就是人类注意力的狭隘性,也就是当集中度 增加时,人类注意力往往变得更加专注。随着人类越来越专注于手头上的工作,打破这种专注 也越来越困难。1972年, 一 架商务飞机因为飞行员太过专注于降落器的问题(实际上,是在改 变降落齿轮指示灯的过程中),尽管当时在驾驶舱里的警报已经响了,飞机还是笔直地撞向地面, 造成了空难。 个人计算机的界面中经常会出现一些小状况。比如,大多数键盘提供大小写灯,这是为了 显示键盘处在大写键锁定模式下(即“大写锁定”键被按了)。但是,如果有人不小心按了大小 写按键,直到奇异的字符出现在屏幕上,用户才会注意到灯的变化。即使如此,用户依然会迷 茫一会儿才会发现问题的原因。从某种意义上来说,用户看不到大小写灯的变化是很正常的, 因为键盘的指示灯不在用户的视线范围之内。但是,通常用户也不能注意到直接放置在他们视 线中的指示灯。比如,用户会专注于他们的工作而无法发现显示器上光标的形状变化,即使观 察光标是他们的工作之一。 还有另外一个在界面设计阶段必须预先考虑的人类特质,即并行处理多个事情时有限的思 考能力。在1956年《心理评论》的一篇文章中 , George A . Miller 的研究表明,人类大脑在同一 时间最多处理7个细节问题。因此,界面被设 计成: 当决定需要时,界面上要呈现所有相关的信 息,而不应依赖人类用户的记忆,这是非常重要的。特别地,若要求人类记住先前屏幕图像中 的精确细节,这就是很糟糕的设计。更进一步地,如果界面需要用户在屏幕图像间进行大量导 航,用户会变得很迷惑。因此,屏幕图像的内容和安棑成为了一个重要的设计问题。 238 第 7 章软件工程 尽管人体工程学和知行学的应用使得人机界面设计折射出独特的韵味,但这个领域还是包 含着软件工程中很多更加传统的主题。特别地,搜索度量在界面设计领域和更传统的软件工程 领域中具有同样的重要性。界面可以度量的特性包括了解一个界面所需的时间、在界面上完成 任务所需的时间、用户界面出错的概率、 一 段时间不用后用户使用界面的熟练程度,甚至是一 些诸如用户对界面喜好程度的主观特性。 GOMS 模型最初在1954年提出,它是人机界面设计领域里度量搜索的范例。这个模型的基 础方法论是依据用户的目标(如删除文档中的某个字)、操作(如单击鼠标按键)、方法 C 如双 击鼠标,然后按删除键)和选择规则(实现相同目标的两种方法间的选择)分析任务。实际上 这就是 GOMS 缩写的起源 goal (目标 )、 operator (操作 )、 method (方法)以及 selection rule (选择规则)。简言之, GOMS 就是一种把用户使用一个界面的动作分析成基本步骤序列(按键、 移动鼠标和作出决定)的方法论。每个基本步骤的性能都被赋予一个精确的时间段,这样通过 把任务中赋予每个步骤的时间相加,从完成相似任务每个界面所需的时间这个角度来看 , GMOS 提供了一种比较不同的提议界面的方法。 理解类似于 GMOS 的系统的技术细节不是我们当前的研究目的,我们示例的要点是 , GOMS 以人类行为(移动手、作岀决定等)特性为基础。事实上, GMOS 的发展起初被认为是心理学 中的主题。这样 GMOS 重新强调了人类特性在人机界面设计领域中,以及在那些从传统软件工 程延伸的主题中所起的作用。 在可预见的未来,人机界面设计肯定是一个活跃的研究领域。处理当今 GUI 的许多问题依 然没有得到解决,大量附加问题潜存于三维界面的使用中(这样的 3 D 界面已经出现)。实际上, 因为这些界面承诺结合语音和与三维视觉的触摸交流,所以潜在问题的范围是巨大的。 1. a . 说出人机界面设计领域中的人体工程学的应用。 _ , 批微出 人机界面设计頜域中的知行学的应用> _ : 邕為智能肇机与台式巍的人机界菌^ 一个显墓_儷是滚动显示区_运用的挂术。.在台式机上, 滚动通常逄球用鼠标雖隶 犀示区 域下方或右侧:的滚动条来实现,而智谶手机通蕾不使 用滚动 条“就 . 箅® 掛' 它们渾帶也遽示為鲫良::以表明:当前榦;部 夯可见 J : 因此, :智戴手减彝 窗止的辮鈇通 k 在 a . 根据人体工程学,:可以提出什么论据来支持这一区别? 1). 根据知学,可以提出什么檢据来支持这一区别? - :- ' 3^入机界面读计与更 传赛的 软件工程领域有什洛不同? ’ : : >:: - :; 祀说出 在设翁人机界面_考虑的人类的3个特征。 „ 7.9 软件所有权和责任 大多数人都会同意这样一个观点,即公司或个人投资开发髙质量的软件,都可以从中获利, 得到回报,否则就很可能没有人愿意从事开发社会所需的软件的工作了。简言之,软件开发者 需要对他们生产的软件拥有一定的所有权。 提供这种所有权的法律措施归类于 知识产权法, 其中许多依据的都是完善确立的版权法和 专利法原则。实际上,版权和专利的目的是允许“产品”的开发者在向公众发布产品时,其所 有权得到保护。因此,某一产品的开发人员(无论是个人还是公司)应在其创作的所有作品中 13 软件所有权和责任 239 加入版权声明,并以此来声明其对该产品的所有权。(这里的作品包括需求规范、设计文档、源 代码、测试计划和最终产品。注意,版权声明要放在最终产品的某个显著位置。)版权声明清晰 地确定了物主身份、被授权使用该产品的人员及其他限制。此外,开发人员的权利通过 软件许 可 (software license ) 中的法律条款正式表述。 软件许可是软件所有者与软件用户之间的一份法律协议,它为用户使用这一产品授予一定 的使用许可,但不允许转让知识产权。这些协议非常详细地解释了各方的权利和义务。因此, 在安装和使用某一软件产品前,仔细阅读和理解软件许可中的条款是非常重要的。 尽管版权和软件许可协议为禁止直接复制和非授权使用软件提供了法律保护,但它们通常 不足以禁止另一方独立开发与该产品功能几近相同的软件产品。令人遗憾的是,多年来很多真 正具有创新性的软件产品的开发人员却无法从他自己的发明中充分获利(其中两个著名的例子 便是电子制表软件和 Web 浏览器)。在大多数情况下,往往是另一家公司成功地开发了具有竞争 力的产品,并占据了具有绝对优势的市场份额。就这一方面来说,阻止竞争对手侵扰的一个法 律依据就是专利法。 专利法的建立是为允许发明者从他的发明中获得商业上的利益。为了获得专利,发明者必 须透露发明的细节,并说明这是新的、有用的,并且对于类似背景下的其他人不是轻而易举做 到的(对于软件来说,这一需求非常具有挑战性)。如果一个专利被授权,那么在一段有限的时 期内发明者就被赋予了这样的权力,即防止其他人制造、使用、销售或引入专利。这段时间一 般是专利申请被提出之日起的20年。 采用专利的一个问题是,获取专利是一个昂贵的、费时的过程,通常历时几年。在这段时 间内,软件产品可能已经被淘汰了,而直到专利被授权,申请者手中只有靠不住的权限去阻止 别人盗用其产品。 在软件工程过程中,明确版权、软件许可和专利是极其重要的。在开发某一软件产品时, 软件工程师通常会集成取自其他产品的软件,它可能是一个完整的产品、一部分组件,或者是 通过因特网下载的一部分源代码。然而,如果在此过程中未能尊重知识产权,则将可能导致巨 大的损失和严重的后果。例如,2004年一个不太出名的公司 NPT 成功地赢得了一场法律诉讼, 它控告 RIM (Research In Motion , 黑莓智能手机制造商)在邮件系统中侵犯了它的一些关键技 术的专利权。这一诉讼的判决结果包括暂停 RIM 向美国所有黑莓用户提供电子邮件服务的禁令! 最终, RIM 与 NPT 达成协议并支付给 NPT 总共 6.125 亿美元,从而避免了关停的命运。 最后,我们应当提出责任的问题。为了使自己免于责任,软件开发者通常会在其产品上附 带免责声明,用以说明其责任的限制。诸如“因使用本软件所造成的任何损失,本公司概不负 责”这样的声明比较常见。然而,如果控方能够举出被告的疏忽之处,法庭很少会认可这类声 明。所以,责任案件容易集中在被告是否对生产的产品给予了相应的关照程度。一个在开发字 处理系统的情况下认为可以接受的关照程度,如果放在核反应堆的控制软件的幵发上,就可 能被认为是一种疏忽。因此,对软件责任声明的最好防护之一就是,在软件的开发过程中, 运用合理的软件工程准则,釆用与软件应用相适应的关注程度,产生并维护验证这些努力的 T 己录。 问题与练习 1. 窍需求_范、设计文挡、,襻代碍和最蜂产昴‘,权声明 的意叉 爆件么? 3. 莬责 声明中 的什么会 M 磕庭 ii 可? ' 1 1 .厂 240 第 7 章软件工程 复习题 (带 * 的题目涉及 选读小 节的内容。) 1. 举出一个例子,说明软件开发时所做的努力是 如何在日后的软件维护中得到回报的。 2. 什么是演化式原型开发? 3. 试解释缺少度量某些软件特性的度量学是如 何影响软件工程学科的。 4. 你是否认为度量软件系统复杂性的度量标准 是积累的?积累的意思是^整个系统的复杂性 是其各部分的复杂性之和,解释你的答案。 5. 你是否认为度量软件系统复杂性的度量标准 是可交换的?可交换的意 思是: 如果系统最初 开发了 X 特性,后来加入了 Y 特性,或者是如 果原先开发了 Y 特性,后来增加了 X 特性,那 么整个系统的复杂性是相同的,解释你的答 案。 6-软件工程是如何区别于诸如电子、机械工程之 类传统工程领域的? 7. a . 给出软件开发中采用传统瀑布模型的缺点。 b . 给出软件开发中采用传统瀑布模型的优点。 8. 开源开发是一个自顶向下或者自底向上的方 法学吗?请解释你的答案。 9- 试描述常量的使用是如何比宇面量的使用更 能简化软件维护的? 10. 耦合和内聚的区别是什么?哪个应该最小 化?哪个应该最大化?为什么? 11. 从日常生活中选取一个对象,依据功能内聚和 逻辑内聚来分析其组成部分。 12. 试将由一条简单的 goto 语句所造成的两个程 序段间的耦合与由过程调用所引起的耦合进 行比较。 13. 在第6章中我们已经知道,参数可以通过按值 传递或按引用传递这两种方式传递给过程。哪 一种提供了更为复杂的数据耦合形式?请解 释你的答案。 14 . 如果一个大型的软件系统中的数据元素都设 计成全局数据,那么在维护阶段中可能会出现 什么问题? 15 . 在面向对象程序中,声明一个实例变量是公 有的或是私有的对数据耦合意味着什么?对 于声明实例变量为私有的这一偏好,其背后的 基本原理是什么? *16 举出一个涉及并行处理环境下发生的数据稱 合的 问题。 17. 按如下结构图回答下列 问题: a. 模块 Y 把控制返回到哪个模块? b . 模块 Z 把控制返回到哪个模块? c. 模块 W 和模块 X 是通过控制耦合链接起来的 吗? d. 模块 W 和模块 X 是通过数据耦合链接起来 的吗? e. 哪些数据是由模块 W 和模块 Y 共享的? f . 模块 W 和模块 Z 以什么方式相关联? 18. 用一个结构图来表示为小商店(也许是在人流 量较大的社区开的一家私人古董店)幵发的一 个简单库存/账务系统的过程结构。请问,出 于营业税的变动,你必须要修改系统中的哪些 模块?如果想给以前的顾客邮寄广告,那么当 你决定要维护一个老顾客的记录时,应该对哪 些模块进行修改? 19. 对上题设计一个面向对象的解决办法,并用一 个类图进行表示。 20- 画出一个简单的类图,表示杂志出版商、杂志 和订阅者之间的关系。其实,只要在表示每个 类的相应矩形框中描述类名即可。 21. 什么是 UML ? UML 的作用是什么?请详细解 释与 “M” 这一字母对应的词。 22-画出一个简单的用例图,描述图书馆的顾客使 用图书馆的方式。 23. 请画出一个序列图,表示当公用事业机构给客 户发送账单时,继而发生的交互序列。 24. 画出…个简单的数据流图,用来描述当一个 交易完成时,自动库存系统里所出现的数 据流。 25. 请将类图所表不的信息与序列图所表示的信 息进行比较。 26. 请说明一对多联系与多对多联系有何不同? 复习题 241 27 . 请举出一个本章中没有提到的一对多联系的 例子。举出一个本章中没有提到的多对多联系 的例子。 28 . 基于图 7-10 中的信息,想象一下在看望病人的 过程中医生和病人间可能发生的交互序列。画 出表示这个序列的序列图。 29 . 画出一个类图,表示饭店里服务生和顾客之间 的关系。 30 . 请画出一*个类图,用来表 7 TC 杂志、杂志出版商 和订阅者之间的关系。并提供每个类的实例变 量和方法 。 31 . 扩展图 7-5 中的序列图,显示这样的 序列: PlayerA 成功地返回了 PlayerB 的球,但 PlayerB 未能成功返回这个球。 32 . 基于如下类图回答下列问题,类图表示的是 工具、工具的用户以及工具的生产厂商间的 关联。 a . 哪个类( X 、 Y 和 Z ) 表示工具、用户和厂 商?验证你的答案。 b . 工具能被多于1个用户使用吗? c - 工具能被多于1个厂商制造吗? d 是否是每个用户使用由多个厂商制造的工具? 33. 根据下面的各种情况,判断所述的活动是与序 列图、用例图有关,还是与类图有关。 a - 确定用户将与系统交互的方式。 b . 确定系统中类之间的关系。 c . 确定完成某一任务时对象的交互方式。 34. 基于下面的序列图,回答下列问题。 a . 什么类含有名为 ww 的方法? b . 什么类含有名为 xx 的方法? c . 在序列中,“类型”为 Z 的对象会与“类型 为 Y 的对象直接通信吗? 35. 请画出一个序列图,说明对象 A 调用对象 B 中 的方法 bb , B 执行请求的动作并返回控制给 A , 然后 A 再调用对象 B 中的方法 cc 。 36. 扩展对前面问题的解决方法,表明只有当变量 “ continue ” 为真时, A 才能调用方法 bb , 在 B 返回控制后,只要 “ continue ” 继 S 为真 , A 就可以继续调用 bb 。 37. 请画出一个类图,用来描述这样一个事实,即 Truck (卡车)类和 Automobile (小汽车)类 都是 Vehicle (汽车)类的泛化。 38. 基于图7-12,什么额外实例变量会包含在“类 型”为 SurgicalRecord 的对象中?对于类型 为 OfficeVisitRecord 呢? 39. 为什么继承并不总是实现类泛化的最佳方 式? 40. 请举出软件工程领域以外的一些设计模式 D 41. 总结设计模式在软件工程中的作用。 42. 在什么程度上来说,一个典型的高级程序设计 语言中的控制结构(如 if - then - else 、 while 等)就是一个小型的设计模式? 43. 以下情况中,哪个涉及了帕累托法则?并解释 你的答案。 a . — •粒老鼠屎搞坏一锅粥。 b . 每个电台集中于一种特定的形式,如摇滚 乐、古典音乐、谈话节目等。 c . 在选举活动中,候选人非常明智地将其重 点放在之前投他们票的那部分选民上 44. 软件工程师希望大型软件系统在错误的内容 上是同种类型的,还是不同类型的?请解释你 的答案。 45. 黑盒测试与白盒测试的区别是什么? 46. 试举出一些在软件工程以外的领域中发生的 与黑盒测试和白盒测试类似的事件。 47. 开放源码开发与 beta 测试有何区别?(考虑白 盒测试和黑盒测试。) 48. 假定在一个大型系统快要完成最后的测试前, 故意放入100个错误。此外,还假定在最后的 测试期间发现并纠正了200个错误,而其中的 50个错误属于故意放入系统中的。请问,如果 接下来那些剩下的50个已知错误也被纠正了, 那么你估计系统中还有多少个没有发现的错 误?为什么? 49. 什么是 GOMS ? 50. 什么是人体工程学?什么是知行学? 242 第 7 章软件工程 51 . 对比智能手机与台式机的人机交互界面,一个 区别就在于缩放显示屏上图像的技术。在台式 机上,缩放通常通过拖动一个独立于显示区的 滑块来实现,或者通过菜单或工具栏选项实 现^在智能手机上,缩放是这样实 现的: 用大 拇^和食指同时触摸显示屏,然后改变两个触 摸点间的距离。 a . 根据人体工程学,可以提出什么论据来支持 这一区别? b . 根据知行学,可以提出什么论据来支持这 一区别? 52 . 在什么情况下,传统的版权法无法保护软件开 发者的投资? 53 , 在什么情况下,软件开发者不能成功获得专利? 社会问题 下面的问题有助于分析一些与计算领域相关的伦理、社会和法律问题。回答这些问题不是 唯一的目的,还应该考虑为什么这样回答,以及你的判断是否对每个问题都标准如一。 l . a . 分析员玛丽被分配了一个任务,即要实现一个系统。通过该系统可以将医疗档案存放 在联网的计算机上。依据她的观点来看,系统安全性方面的设计存在着缺陷,但是由 于公司财力方面的原因,她所提出的想法被否决了,而且公司要求使用她认为不太合 适的安全系统来继续该项目。这种情况下,她该怎么办?为什么? b . 假设分析员玛丽按照吩咐实现了该系统,而现在她发现有非授权人员在检索医疗档 案。这时她该怎么办?对于这样一种侵犯安全的情况,她将负多大责任? c . 假设分析员玛丽没有听从老板的安排而拒绝再开发这个系统,并且义无反顾地将设计 缺陷公布于众,结果导致公司的财务紧张,许多无辜的员工失去工作。分析员玛丽的 行为对吗?如果玛丽仅仅是整个设计组的一名成员,她并不了解公司正在花费大的精 力在别的地方开发一套有效的安全系统,而这套系统将会用在玛丽正在开发的系统 上。那么又会怎么样?这种情况是如何改变你对玛丽行为的判断的?(需要注意的是, 玛丽对这种情况的观点和以前一样。) 2-当一个大型软件系统由许多人一起开发时,如何分配责任?是否有一种层次型的责任? 是否有不同程度的责任? 3. 我们知道,大型的复杂软件系统通常是许多成员一起开发的,其中很少有人能够对整体 系统有一个全面的了解。那么对于一名员工来说,他对系统的功能没有完全了解,却要 为该项目出力,这么做在道德上是否合适? 4. 某人对其成果最终为他人所用,应当负多大的责任? 5. 在计算机专业人员与客户之间的关系中,专业人员的责任是实现客户的需求,还是对客 户的需求加以指导?如果专业人员预见到客户的要求会导致缺乏职业道德的结果发生, 该怎么办?例如,客户可能为了提高效率希望走捷径,而专业人员预见到如果采用走捷 径的方式,可能会成为产生数据错误或系统误用的根源。如果客户坚持这么做,那么专 业人员是否就没有责任? 6. 如果技术的发展太过迅猛,发明者还未来得及从他的发明中获利,新的发明却已紧随而 来,取而代之。这样将会发生什么?这种获利对激励发明者而言是必需的吗?开放源码 开发的成功与你的答案有什么关系?免费的髙质量软件足以持续支撑现实的需求吗? 7. 计算机革命能否有助于(或者说帮助解决)世界能源问题?对其他的一些大规模问题, 如饥饿和贫穷等,情况又会如何? 8. 技术是否会无限期地发展下去?是否有什么因素会逆转社会对技术的这种依赖?如果社 会继续推进技术无限期地发展下去,那么结果将会怎样? 课外阅读 243 9.如果你有一台时间机器,你想要生活在哪个历史时间段中?是否有你想带走的当前技 术? 一种技术能与另一种技术分开吗? 一边反对全球变暖, 一 边接受现代医学治疗,这 现实吗? 10. 智能手机上的许多应用会自动集成其他应用提供的服务。这种集成可能会将进入一个应 用的信息与另一个应用共享。这种集成的好处是什么?过多地集成会导致什么问题吗? 课外阅读 __ Alexander, C., S. Ishikawa, and M. Silverstein. A Pattern Language, New York: Oxford University Press, 1977. Beck, K. Extreme Programming Explained: Embrace Change, 2nd ed. Boston , MA: Addison-Wesley, 2004. Bowman, D. A., E. Kruijff, J. J. La viola, Jr., and I. Poupyrev. 3D User Interfaces Theory and Practice. Boston, MA: Addison-wesley, 2005. Braude, E. Software Design: From Programming to Architecture. NewYork: Wiley, 2004. Bruegge, B. and A. Dutoit. Object-Oriented Software Engineering Using UML, Patterns, and Java, 3rd ed. Boston, MA: Addison-Wesley, 2010. Cockbura, A. Agile Software Development: The Cooperative Game, 2nd ed. Boston, MA; Addison-Wesley, 2006. Fox, C. Introduction to Software Engineering Design: Processes, Principles and Patterns with UML2. Boston, MA: Addison-Wesley, 2007. Gamma, E., R. Helm, R. Johnson, and J. Vlissides. Design Patterns: Elements of Reusable Object- Oriented Software. Boston, MA: Addison-Wesley, 1995. Maurer, P. M. Component-Level Programming. Upper Saddle River, NJ: Prentice-Hall, 2003. Pfleeger, S. L. and J. M. Atlee. Software Engineering: Theory and Practice, 4th ed. Upper Saddle River, NJ: Prentice-Hall , 2010. Pilone, D. UML 2.0 in a Nutshell Cambridge, MA: O’Reilly Media, 2005. Pressman , R. S. Software Engineering: A Practitioner's Approach, 7th ed. New York: McGraw-Hill, 2009. Schach, S. R. Classical and Object-Oriented Software Engineering, 8th ed. New York: McGraw-Hill, 2010. Shalloway, A. and J. R* Trott. Design Patterns Explained, 2nd ed. Boston , MA: Addison-Wesley, 2005. Shneiderman, B” C. Plaisant, Cohen, M, and Jacobs S. Designing the User Interface: Strategies for Effective Human-Computer Interaction, 5th ed. Boston, MA: Addison-Wesley, 2009. Sommerville, I. Software Engineerings 8th ed. Boston, MA: Addison-Wesley, 2006. 数据抽象 t 章将要研究的是如何对数据组织形式进行模拟,这门学科称为数据结构,它不同于 由计算机内存所提供的以一个个单元来组织数据的方式。其目标是让数据的使用者 将数据集视为一种抽象的工具来访问,而不是从计算机内存中的数据组织的角度去考虑问题。 这方面的研究工作将向我们展示,构造这种抽象工具的需求是如何产生对象和面向对象编程概 念的。 _章_ 8.1 数据结构基础 8.2 相关概念 H 数椐结构的实 II 1 8.4 —个简短案例的研究 8.5 ; 幾制翰:数赫类型 *8,6 类和对象 *8.7 机器语 言中的指针 , _ ... ... 1 . 复獨 社会问题 .: 课外阅读 第6章已经介绍了数据结构这个概念。在那一章中,我们已经了 解到: 程序员可以利用高级 程序设计语言所提供的技术来表示算法,就好像所操作数据的存储方式并不是按照一个个单元 在内存中存放。我们还学习到,编程语言所支持的数据结构称为基本结构。本章将讨论一种技 术,利用这种技术能够构建和操作与语言的基本结构不同的数据结构,该研究能够使我们从传 统的数据结构过渡到面向对象的范型。贯穿这项工作进展的潜在主题是抽象工具的构建。 8.1 数据结构基础 这里,先介绍一些基本的数据结构并作为后续几节的例子。 8.1.1 数组 在 6.2 节中,已经介绍了同构数组和异构数组这两个数据结构。同构数组 (homogeneous array ) 是一种“矩形的”数据块,其项具有相同的类型。具体来说,一个二维同构数组由行与列组成, 其中,项的位置由一对下标确定,即第一个下标值确定项的行位置,第二个下标值确定项的列 位置。例如,用一个矩形数组表示销售人员的每月销售额,每行的项代表的是某个销售人员每 月的销售额,每列的项代表的某个月每个销售人员的销售额。这样一来,第3行第1列的项就可 以表示第3个销售人员第1个月的销售额。 与同构数组不同,异构数组 (heterogeneous array ) 是一个可能具有不同类型的项块。块里 的项通常称为部件 ( compoxient 〉。 例如,用一个异构数组的数据块表示一个员工,其部件可能 有 三项: 员工的名字(字符型)、年龄(整型)以及技能等级(实型)。 8.1 数据结构基础 245 8.1.2 列表、栈和队列 列表 ( list ) 是这样的一组数据,其表项按顺序排列(如图 8- la 所示)。一个表的开头 称为表 头 ( head ), 表的尾端称 为表尾 ( tail )。 几乎所有的数据集合都可以看成列表。例如,文本可以被看成符号的列表,二维数组可 以看成是行的列表, CD 上记录的音乐可以看成是声音的列表。更为常见的例子包括客人清 单、购物清单、班级注册表、存货表等。与列表相关的操作视情况而定。在某些情况下,我 们可能需要从列表中移除项,向列表中增加项,每次“处理”列表中的一个项,改变项在列 表中的排列,或者查找某个特殊的数据项是否在列表中。我们将在本章的后面讨论这些操作。 通过严格限制列表中项的访问方式,我们可以得到两种特殊类型的表,称为栈和队列 。栈 ( stack ) 是这样的一种列表,该表的项只能在表头进行添加和删除(如图 8 -lb 所示)。用通俗的 术语来表示,榜的头称 为找顶 ( top ), 栈的尾称 为找底 ( bottom 或 base ) 。 在拽顶增加一个新的 项称 为入梭 ( pushing ), 在栈顶删除一个项称 为出找 ( popping ) o 注意,最后入桟的数据最先出 栈,这样就可以 得到: 栈是 LIFO ( Last - In , First - Out , 后进先出,读作 “ LIE - foe ”) 的结构。 对于那些检索次序与存储次序相反的存储数据项而言,这种后进先出特性意味着栈是理想 的选择,所以栈经常被用作回溯活动的基础。[术 语回溯 ( backtracking ) 是指退出系统的过程, 它与进入系统的次序相反。一个经典的例 子是: 为了找到走出森林的路径而原路返回。]例如, 思考一下支持递归过程所需的基本结构,在每一个新活动开始时,先前的活动必须保存下来。 而且,在每一个活动结束时,必须检索前一个保存的活动。这样,如果当活动被保存时就会压 入栈中,那么每次需要检索一个活动时,合适的活动将处在栈顶。 队列 ( queue ) 是这样的一种列表,其表项只能从表头删除,新表项只能从表尾插入。这 种数据结构的例子有,戏院门口排队等待购票的一队人(见图 8- lc ), 这里,位于队列头的人 先购票,而新到的人必须到队尾进行排队购票。在第3章中,我们已经遇到过这种数据结构, 在那节中,批处理系统所存放的作业必须在所谓的作业队列中进行排队,等待执行。可以得 出这样的 结论: 与栈不同,先进队列的项会先从队列中删除,就是说队列是 FIFO ( First - In , First - Out , 先进先出,读作 “ FIE - foe ”) 的结构,这意味着表项以它们存储的顺序从队列中删除。 队列 _ Jill- - 表头 Bob 列表一 Devon — Maurice - 表尾 (a) 名称列表 图 8-1 表、栈和队列 正如第1章中介绍的,队列常被用作缓冲区的基本结构,缓冲区是从一处传送到另一处的数 据临时放置的存储区域。当一项数据到达了缓冲区,它就被放置在队列的末尾。当需要转发数 据项到达最终的目的地时,它们按其在队列头部出现的次序被转发。因此,数据转发的次序就 是它们到达的次序。 8.1.3 树 (b) 由书组成的栈 ( c) 由人组成的队列 树 ( tree ) 是这样的一个数据集合,其项具有层次化的组织形式,很像一个典型的公司组织 246 第 8 章数据抽象 关系图(如图 8-2 所示)。这种组织图中,顶部表示总裁,由分支线下连到副总裁,副总裁又连 到地区经理,等等。对树的这种直观性的定义,我们还要加上一个限制性条件,即(参照组织 图)公司的任何一个员工只有一个直接上司。也就是说,组织中的不同分支不会在下一层相遇。 ( 第6章已经举过几个树的例子,是以语法分析树的形式介绍的。) 图 8-2 组织图的一个例子 树中的每一 个位置 称为一 个节点 ( node ) (如图 8-3 所示)。树顶部的那个节点称为 根节点 (root node ) ——如果我们把图倒过来看,这个节点就表示了树的根。另一端点处的节点称为 终端节 点 ( terminalnode ), 有时也称为叶 子节点 ( leafnode )。 我们常将从根到叶子的最长路径上的节 点数称为树的深度 ( depth ) 0 换句话说,一个树的深度就是该树所包含的层数。 /根节点 鼸终端节点(或叶子节点) 图 8-3 树的术语 有时候,我们会提到这样一种树结构,该树的每个节点派生出其直接下层的节点。.所以常 常会说到一个节点的祖先和后代。这里,称一个节点的直接后代为子 ( children ) 节点,称其直 接祖先为父 ( parent ) 节点。而将有同一个父节点的那些节点称之为兄弟 ( sibling ) 节点。如果 一 1 个树的每个父节点有不多于两个的子节点,那么称该树为 二叉树 ( binarytree )。 如果选择一棵树中的任意一个节点,该节点与其下层的那些节点也构成了一个树结构,那 么就称这些较小的结构 为子树 ( subtree )。 这样一来,每个子节点就是其父节点下面的子树的根 节点,这样的子树称为父节点的一个分支 ( branch )。 在二叉树中,提到树的显示方式时我们经 常会谈到, 一 个节点的左子树和右子树。 8.2 相关概念 247 问题与练习 1. 钟对以下每一个结构: 列表' 桟 c 队列和树,#岀猶子 ( i 十募钒科学臥舛的乂 ::;:::八, 2. 总结出列表、栈及队列伺的区别。 _ :- : f 3. 假设 A 字母被放入一个空找:中,然后依次是字母 BJPC , 再假设一■个字梭 Y . 字釋 D . 初 E 入検。请将 ' 桟中字母按照 出瑰的自项 向 T 的顺序排列®來,如廉一个字母婆出: 桟:,駟个 誓母将梦橙索? 4. 假设字母 A 放入一个空的队列中,然后依次是字母 B 和 CU 再 値设此 队列中^个字舟被移出,.之后插 入字母 D 和艮请按照字母在队列中从表头 到表尾 岀现的顺序列出它们 。 如果此时再要承队列中移出 ' 一个字母,应读是哪个字母? , 1 '■ 1 1 v . - ; ■ ■ 5. 假设一个树有4 个 节点: _ A 、 B 、 C 和 IX 如果 A 和 C 是兄弟, 而 D 的父节.点; ^ A , 赛 些节点是叶子节点? 哪些节点是稂节点?, ' : - : % ''乂 . •• - •• ,• •• ' I ' , • : . : . :::: ;| •••• : , .-;•••• 1 :. ' : . • ■; •'':;;•:..: :; : \. : :Y : : : .•: 8.2 相关概念 __ 在本节中,我们分别讨论3个与数据结构紧密相关的 主题: 抽象、静态与动态结构间的区别 以及指针的概念。 8.2.1 抽象 前面小节中出现的数据结构常与计算机内存中所存储的数据有关。但是计算机的内存并不 是按照数组、列表、栈、队列和树这样的结构来组织的,而是顺序地组织成一组可寻址的存储 单元。这样一来,所有的其他结构都必须进行模拟。如何完成这种模拟工作是本章的主题。到 现在为止,我们只是指出,数组、列表、栈、队列和树这样的组织都是些抽象工具,之所以构 造这些抽象工具,是为了使数据的用户不用关心实际数据存储的细节,这样信息就好像是以一 种更为方便的形式进行存储的,便于用户访问。 在这里,用户这个术语并不一定指人,这个词的含义因视角和时间而异。如果从一个使用 pc 机来维护保龄球比赛记录的人的角度考虑,那么用户就是一个人。在这种情况下,应用软件 (也许是电子制表软件包)将负责把数据用人觉得方便访问的抽象形式表示出来(很可能用同构 数组来表示)。如果从因特网上的一个服务器的角度考虑,那么这时的用户可以是一个客户端。 在这种情况下,服务器将负责把数据表示成便于客户端访问的抽象形式。如果从程序的模块结 构来考虑,那么用户应该是需要访问这些数据的任何模块。在这种情况下,模块所包含的数据 应该负责把数据表示成便于其他模块访问的抽象形式。所有这些情况中,有一条共同的主线, 那就是用户拥有将数据作为一个抽象工具来访问的特权。 8.2.2 静态结构与动态结构 构建抽象数据结构中的一个重要区 别是: 所模拟的结构是静态的还是动态的。也就是说, 结构的形状或大小是否会随时间改变。例如,如果这个抽象工具是一个名字列表,那么考虑以 下情况将非常 重要: 这份名字列表是会一直保持固定的大小,还是可能因名字的增加和删除而 扩大和缩小。 就一般规律而言,静态结构比动态结构更容易处理。如果一个结构是静态的,那么仅仅需 要提供一种能够访问结构中不同项的方法就可以了,也许就是能改变指定位置的数据值的方法。 但是,如果结构是动态的,那就必须要处理增加和删除项的问题,还要找到因数据结构增长所 248 第 8 章数据抽象 需的存储空间。在结构设计不合理的情况下,增加一个新项可能会导致对结构进行大规模的重 排,而且结构的过度增长可能会迫使整个结构转移到另一个可用空间更大的存储区域。 8.2.3 指针 我们知道计算机内存中各种不同的单元是由数字地址来标识的。作为数值,这些地址本身 就可以进行编码,存放在内存单元中。指针 ( pointer ) 是一个存储区,包含了这样的被编码过 的地址。对于数据结构,指针用来记录数据项存放的位置。例如,如果我们必须要不断地将一 个数据项从一个位置移到另一个位置,那么可以指定一个固定的位置,将其作为指针。这样一 来,每次移动该项时,就能够通过更新这个指针来反映数据的新地址。接下来,当要访问该数 据项时,就可以通过指针来找到该项。事实上,指针将一直指向数据。 在第2章学习 CPU 的过程中,已经遇到过指针这个概念。在那一章中,我们可以看到, 称为程序计数器的寄存器被用来存放下一条要执行的指令的地址。所以,程序计数器就起到 了指针的作用。事实上,程序计数器的另一个名字叫作指令指针 ( instructionpointer )。 举一个指针应用的例子,假设在计算机内存中,按书目的字母顺序存放着小说的清单。虽 然在许多应用中,这样的安排比较方便,但是如果要寻找某个作者的所有小说作品就比较困难 了,因为它们分散在整个列表中。为了解决这个问题,可以在表示每本小说的存储单元块中保 留一个额外的存储单元,并将该存储单元用作一个指针,指向表示同一作者另一本小说的存储 块。通过这种方法,同一作者的所有小说就可以链接成一个环(如图 8-4 所 示)。 找到给定作者 的一本小说以后,我们就可以循着指针一本接另一本地找到该作者的所有小说。 图 8 _4按书名排列而根据作者链接的小说 现代的许多程序设计语言都把指针作为一种基本的数据类型。也就是说,就像对整数、字 符串那样,程序设计语言也可对指针进行声明、分配以及操作。利用这种语言,程序员就能在 计算机存储器中,把相关的项用指针相互链接起来,从而设计出精巧的数据网。 i , 数组、■表 S 栈、祕列和树等数_结抅在健稀 :霉义上是抽 攀的?〈,:,_ 2 •,猜 •出一个渉及静•数辑綠构应用的俩于 再举出 一个涉及动态藪锯结构删的 ㉖ 子 3. 硪举出在计算机科学领域外出现^针这个概念的柄子, ― 1 : ...... . .'':••• I'ffvj. 1 ' : ::,;:• :' l 1 " 1 : :' :;;•' V :; ,- :: ; V ,-r ;:•]!;, :V ' : ::卜 1 : '; '• : . .L ::-:"■' : : .; ..... 卜 1 ; _ ,: ,- ..... : ':•• i ': ':• ••'•■ V 1 •' !. ! I :: -:: I ; •• , •, •, __ ......乂 . 卜..._ .... . V :' . 5:. 酿* 8.3 数据结构的实现 现在我们来讨论 8.1 节所介绍的一些数据结构在计算机内存中的存储方式。 8.3.1 数组的存储 我们首先讨论存储数组的技术。正如第6章所介绍的,在高级程序设计语言中,常常将这些 8.3 数据结构的实现 249 结构作为基本结构来提供。在此,我们的目标就是要理解如何将处理这些结构的程序翻译成用 来处理存放在内存中的数据的机器语言程序。 _ 1. 同构数组 假设要存储一个24小时温度的读数序列,每个读数需要存储空间的一个存储单元。而且, 假设依据它们在序列中的位置来确定这些读数。也就是说,我们要能够访问第1个读数或者是第 5个读数。简单来说,就是要按照一维同构数组的方式来处理这个序列。 这里,只要将这些读数按顺序存放在具有连续地址的24个存储单元中,就可以实现这个目 标了。那么如果这个序列中第1个单元的地址是: c , 那么任何一个指定温度读数可以这样计算得 到,即用所要读数的序号减去1,然后将计算的结果加上 X 。具体来说,第4个读数就放在 (4-1) 这个地址中,如图 8-5 所示。 地址 - p x x 十 1 x + 2 x + 3 x + 4 x + 5 x + 6 存储单元—— Readings[1] Readings[2] Readings[ 3 ] Readings[4] 图 8-5 存放在存储器中的温度读数数组,起始地址为 x 这种技术为大多数高级程序设计语言的翻译程序所采用,用以实现一维同构数组。当翻译 程序遇到下面这样的声明语 句时: int Readings[24 ] ; 这就表明, Readings 这个术语是指可以存放24个整数的一维数组,这时,翻译程序就会安排预 留24个连续的存储单元。以后在程序中,如果遇到赋值语句 Readings[4] 一 67; 则要求将值67放入数组 Readings 的第4项中。此时,翻译程序就生成了一串机器指令,把值67 放入地址为 x + (4-1) 的存储单元中,其中 jc 为与数组 Readings 相关的存储块中第一个单元的地 址。通过这种方式,程序员在编写程序的时候,就可以认为温度读数确实存放在一个一维数组 中。(注意,在 C 、 C #、 C # 及 Java 语言中,数组的下标是从0而不是从1开始的,这样一来,第4 个读数应该由 Readings [ 3 ] 表示。见本节末的问题与练习3。 ) 现在,假设我们要记录一个公司的销售人员一周内的销售业绩。在这种情况下,可以想象 将数据组织成一个二维同构数组。在该数组中,每行的值表示某个员工的销售业绩,而每列中 的值表示某一天内所有的销售业绩。 为了实现这种要求,首先要认识到这个数组是静态的,即使它的内容得到更新,其大小也 不会改变。所以就可以计算出存放整个数组所需的存储区的总数,然后就保留这样大小的一块 连续存储单元。接下来就一行一行地把数据存入数组,从所保留的存储块的第1个存储单元起, 把数组第1行数值存进连续的存储 单元; 接着存放下一行,再下一行,以此类推(如图 8-6 所示)。 这样一种存储系统称为 行主序 (row major order ) 系统。与之相反的是,如果数值一列接着一列 地存放,则称 列主序 (column major order ) 系统。 ■7 二 - - V- r : 二、 ,■办 ,- ' 4 ' =, r , , r-,f 績■: .cr , U_ ;. Ji : ,卜 r KO 1 ': 1 . - -1 r " - r r-"- - A — '以 250 第 8 章数据抽象 概念数组 ~ i ~ f ~~ 1 1 \ 1 1第 H 丁 i ; krh 1 1 _ 瓣 9 f ;科 1 I 计算机的存储器 1 1 \ \ \ 第2行 | ■ lit !:參 1 第 4 行 | 1 I 1 I 第 3 行第 4 列的项 图 8-6 以行主序方式存储的一个4行5列二维数组 如果数据以这种方式存放,那么考虑一下,如何找到数组中第3行第4列的数值?设想一下, 我们处在所保留的机器存储块的第1个单元。从这个位置起,可以依次找到数组第1行的数据, 接着是第2行,然后是第3行,依次类推。要得到第3行的数据,我们必须先经过第1行和第2行。 由于每一行有5个项 C 星期一至星期五,每天一个项),因此要访问到第3行的第一个项,必须经 过一共10个项。从那起,我们还必须再经过3个项,才能到达第3行第4列的那个项。这样,为了 到达第3行第4列的项,从存储块的开始处总共需要经过13个项。 上述的计算过程可以概括为一个公式,即可以将行列位置的索引转换为实际的存储器地址。 具体来说,如果令 c 表示一个数组的列数(也就是每行所包含的项的个数),那么第/行第/列项的 地址就可以表 示为: : c+(cx(/-l)) 十 (/-l) 其中, x 是第1彳丁第1列项的单兀地址。也就是说,必须经过/- I 行(每行包括 c 个元素),才能到 达第 珩, 然后再经过尸1个项,才能到达这行的第/项。上面的例子中,^5, ^3,户4,所以,如 果数组从地址行存放,那么第3行第4列的项的地址就应该为 x +(5 x (3- l ))+(4- l)=x + 13。 表 达式 ( cx ( z ‘- l ))+(/- l ) 有时候称为地址多项式 ( addresspolynomial )。 这也是大多数高级程序设计语言的翻译程序所釆用的技术。当遇到声明语句 int Sales [8,5] ; 时,则 表明: Sales 是一个 8 行 5 列的二维整数数组,翻译程序就会留出 40 个连续的存储单元。 以后如果遇到赋值语句 Sales[3,4] — 5; 则需要将数值 5 放到数组 Sales 的第 3 行第 4 列的那个项中,此时,就产生一串机器指令,将数值 5 放 到地址为 x +(5 x (3- l ))+(4- l ) 的存储单元中,其中, x 是与数组 Sales 相关联的存储块的第 1 个单元的 地址。通过这种方式,程序员编写程序时,就好像销售量确实存放在一个二维数组中一样。 2. 异构数组 现在,假设要存储的异构数组称为 Employee , 该数组包含3个 部件 : Name (字符型 ), Age (整型 ), SkillRating (实型)。如果数组中的每个部件所需的存储单元的数目是固定的,那 么就可以将数组存放在一个连续的单元块中。例如,假设 Name 部件最多需要25个单元, Age 只 需要一个单元, SkillRating 也只需一个单元。于是,我们就可以预留出一个有27个连续单元 的存储块,开始的25个存储单元用来存放员工的名字,第26个存储单元用来存放员工的年龄, 最后一个存储单元用来存放员工的技能等级 ( 如图 8-7 a 所示)。 8.3 数据结构的实现 251 Employee Employee .Name Employee , Age Employee ..SkillRating 地址: + 25 x+ 26 ( a ) 存放在一个连续存储块中的数组 指针一^ Employee.Name Employee.Age Employee -SkillRating ( b ) 存放在不同位置的数组部件 图 8-7 存储同构数组 Employee 通过这种安排,就可以很容易地访问该数组中不同的部件。例如,如果第一个存储单元的 地址是 X ,那么指向 Employee .Name (意思是 Employee 数组中的 Name 部件)的任何引用都将 转移到从地址 x 开始的 25 个存储单元,而指向 Employee.Age (意思是 Employee 数组中的 Age 部件)的引用将转移到地址为 x +25 的存储单元。具体来说,如果翻译程序遇到了髙级语言中的 这样一条语句: Employee . Age -*- 22; 那么只要产生一系列机器语言指令,用以将数值 22 放入地址为 x +25 的存储单元。或者说,如果将 EmployeeOfMonth 定义为一个与之类似的数组,并将其存放在地址为 y 的存储块上,那么语句 Emp 1 oy e e0 fMonth — Employee ; 将会翻译成一个指令序列,将起始地址为 x 的 27 个存储单元的内容复制到起始地址为 y 的 27 个存储 单元中。 在一个连续存储单元块中存储异构数组的另一种方法就是,将异构数组的每个部件分别存 放在不同的位置,然后通过指针的方式将它们链接在一起。更准确地说,如果这个数组包含有 3 个部件,那么就在存储器中找到一个位置,用以存放 3 个指针,每个指针指向一个部件(如图 8-7b 所示)。如果这些指针存放在以 x 为起始地址的存储块中,那么通过存放在地址为 x 处的指针就可 以找到第 1 个部件,通过存放在地址 x+1 处的指针就可以找到第 2 个部件,以此类推。 这种存储方式在有些场合尤其适用,如数组部件的大小是动态的情况。举例来说,利用这 种指针系统,只需要在存储器中找到一个存储区来存放较大的部件,然后调整相关的指针,令 其指向这个新位置,这样就可以增加第一个部件的大小了。但是,如果数组是存放在一个连续 的存储块中,那么不得不修改整个数组。 8.3.2 列表的存储 现在来讨论将一个名字列表存放在计算机内存中的技术。一种方法就是将整个列表存入具 252 第 8 章数据抽象 有连续地址的一整块存储单元中。假定每个名字不超过8个字母,我们可以把这个大的整块存储 单元分成一组子块,每个子块包含有8个存储单元。每个子块中放入一个用 ASCII 码记录的名字, 一个单元放入一个字母。如果一个名字不足填满分配给子块的所有存储单元,只需用空格的 ASCII 码将剩余的单元填满就行。利用这种方式,存放一个10个名字的列表需要一个有80个连 续单元的存储块。 图 8-8 所概括的就是这种存储系统。其重点就在于,整个列表都存储在一大块内存中,其连 续的项依次存放在相邻的存储单元中。这样的一种组织称为 邻接表 (contiguous list )。 存储单元的连续块 1 _ 」L _ ' , .- .A. V-:- ' ' - W - _ 根节点的值 (应用 Search 过程查看 TargetValue 是否在根的右子指针标识 的子树中,并报告搜索的结果) )end if 图 8-21 二分搜索用于作为链式二叉树实现的表 图 8-22 利用图 8-21 中的过程搜索字母 J 所涉及的相继变小的树 你也许会这样 认为: 当把“表”存储为一个二叉树时,按照字母顺序对这个表进行打印的 过程将会很困难。然而,为了能按照字母顺序打印出这个表,这里只需要先按字母顺序打印出 左子树,然后打印出根节点,接下来再按字母打印出右子树即可(如图 8-23 所示)。因为,左子 树所包含的元素都小于根节点的值,而右子树所包含的元素都大于根节点的值。到目前为止, 该算法的逻辑框架如下 表示: 8.4 一个简短案例 261 if (树非空) then (按照字母顺序打印左子树; 打印根节点; 按照字母顺序打印右子树) 1. 按字母顺序打 2. 打印根 3. 按字母顺序打 印左分支 节点 印右分支 A, B, C, D, E, F, G, H, I, J 图 8-23 按字母顺序打印出一个查找树 随着动态数据结构的增大或缩小,存储空间也被占用或释放。回收不用的存储空间以备 將來使用,这样一个过程称为垃 圾爾收 (garbage collection )L 许多场合都用到了垃挺铒收机 制。操作系统里的内存管理赛序在分配和回收存储空间时必须执行垃嫁回收工作。文件管理 程序在计算机的大寄量舞储筹上进行文件的奇放和删除操作时,' 為要 执行垃槔回收;作。此 夕卜,在分派程序控制下 i 行的任何进程,在给其^分配的存储空间中也嶔要我#亍最 k 回收工作。 垃圾回收涉及一些难以捉摸的问题。对于链式结构,每当一个指向彀据部 的碑针 或变_, 垃圾回收程序必须决定是否要回收指针原先指向的那个4命查间。在涉犮肴多路極推针失叉 的数据结构中,这种问题就尤为复杂。不准确的垃圾回收例程会导致数据的丟失,或着是存 储空间的利 w 率聲低。例批,弇果抵棘,,体辨作不能成功地码:_储宾间那:么 空『_就会越▲越小,这种现秦称为内存泄_ ( m 如 nory leak L 这个框架中包含按照字母顺序打印左子树和右子树这两项任务,这两项任务本质上是原始 打印任务的缩小版本。也就是说,打印一个树涉及打印子树的任务,这就使人想到运用递归方 法来解决树的打印问题。 依据这条线索,可以把原先的设想扩展为打印二叉树的一个完整的伪代码过程,如图 8-24 所示。这里,将该例程命名为 PrintTree , 然后再调用 PrintTree 来打印左子树和右子树。注意, 因为连续的递归过程中,每次递归所操作的树都要比启动递归的那个树要小,因而,递归过程 的结束条件(遇到一个空子树)肯定会达到。 procedure PrintTree (Tree) if (Tree 非空) then (对以 Tree 中左分支出现的树应用 PrintTree 过程;打印 Tree 的根节点;对以 Tree 中右分支出现的树应用 PrintTree 过程) 图 8-24 用于打印二叉树中数据的过程 262 第 8 章数据抽象 在树中,插入一个新项的工作比起初看起来的也要容易。凭直觉也许会认为,插入新项只 需先将树切开,为新项留出空间,但实际上,所添加的节点不论其值如何,只要作为一个新的 叶子节点就可以将其插入到树中。为了给新项找到合适的位置,要沿着查找该项而遵循的那条 路径往下走。由于该项并不在树中,所以我们将会查找刮一个 NIL 指针。这个位置就是要存放 新节点的合适位置(如图 8-25 所示)。事实上,找到的这个位置正是寻找新项所到达的地点。 H J ( a ) 为这个新项寻找一个空位置 B H 这就是这个新项所应存放的位置 图 8-25 把项 M 插入到其表项 B、E、G、H、J、K、N、P 以树结构存储的表中 对于链式树结构,这个处理过程的程序如图 8-26 所示。这里,首先对树进行搜索,找到要 插入的值(称为 NewValue), 然后再把包含有 NewValue 的一个新叶子节点放到相应的位置。注 意,如果在搜索过程中发现要插入的项已经在树中,则不必进行插入操作。 可以得出这样一个 结论: 包含了链式二叉树结构以及用于查找、打印、插入操作的这些过 程的软件包提供了一个完整的包,该包可以作为我们假想应用的一个抽象工具。事实上,如果 实现得恰当,可以使用这个软件包而无需关心底层的实际存储结构。利用软件包中的过程,用 户可以想象出按字母顺序存放的名单表。而事实上,这个表的项都分散在不同的存储单元块中, 并链接成一个二叉树。 问题与练习 1* 画 一 个二文树,要求该树可以用来存放表 Rs S、T、U、 V、 W、 X、 Y 和 Z, 以备将来搜素之用。 2, _要说明—图^21中的二分搜索算法在应用到图 8,2Q 中的柯时,为查找项 J 所经历的路径。查找项 P 时 的路径又是什么? 3* 画一个廚,用来表示图 8 _2 4 中打印#的递归算法用在图 8-20 所示的有序树中打印 K 节点时的活动状况。 4. 一个树结构,它的每个节点有沉个子节点,试解释这样一 个绰枸 是如何对英资中的拼写正确的词汇 进行编码的 。- 8.5 定制的数据类型 263 procedure insert (Tree,NewValue) if CTree 的根指针 = 叫 1_) ( 设置根指针指向包含 NewValue 的新叶子节点) else ( 执行与相应情况对应的指令块) case 1 :NewValue = 根节点的值 ( 什么也不做) case 2:NewValue i =;k~ru==.^rL. ~ - 'rV. …- 二 … C 二 W •-r . - • • - 8.5 定制的数据类型 在第6章中,我们己经介绍过数据类型的概念,并讨论了如整型、实型、字符型及布尔型等 基本数据类型。大多数编程语言都提供了这些基本数据类型。在本节中,我们讨论这样的--种方 式,即程序员可以定义自己的数据类型,以便能更好地满足某个具体应用的需要。 8.5.1 用户自定义数据类型 如果除了编程语言中提供的那些基本数据类型外,还有其他数据类型可以用,那么对于表 达一个算法来说,通常就比较简单了。基于这种原因,现代的许多编程语言都支持程序员利用 基本数据类型作为构件块,来定义一些附加的数据类型。这些“自制”的数据类型的最基本的 例子称为 用户自定义数据类型 ( user-defined data types ), 其本质就是几个基本数据类型组合而 成的具有同一名字的聚合体。 为了对此进行解释,这里假设要开发一个涉及许多变量的程序,而每个变量都有相同的异 构数组结构,该结构中包含有名字、年龄以及技能级别。 一 种方法是将每个变量分别定义成异 构数组(参见 6.2 节)。然而,更好的办法是将这个异构结构数组定义成一种新的(用户自定义 的)数据类型,然后就把这种新的数据类型作为一种基本类型来使用。 为了实现上述想法,这里我们釆用下列伪代码语句的形式来定义一个称为 EmployeeType 的 新类型。 define type EmployeeType to be {char Name[25]; int Age; real SkillRating; 264 第 8 章 数据抽象 这个异构结构中包含的组成元素有 Name (字符型 )、 Age (整型)以及 SkMIRating (实型)。 这样一来,就可以采用与基本数据类型相同的声明变量的方式,用这个新的数据类型来声明变 量。也就是说,大多数编程语言都用语句 int x; 来声明变量 x 为整数。同样,变量 Employeel 也可以采用如下的语句来声明为 EmployeeType 类型 : EmployeeType Employeel ; 于是,在以后的程序中,变量 Employeel 就将引用一整块存储单元,该存储块中包括员工 的名字、年龄和技能级别。存储块中的各个项可以通过诸如 Employeel . Name 和 Employeel .Age 这样的方式来引用。所以,语句 Employeel .Age 一 26 ; 会被用来将值26赋给 Employeel 块中的 Age 元素。而且,语句 EmployeeType DistManager, SalesRepl , SalesRep2; 可以用来将 3 个变量 DistManager 、 SalesRepl 和 SalesRep 2 声明为 EmployeeType 类型,正如 如下形式的 语句: real Sleeve, Waist, Neck; 通常被用作将变量 Sleeve 、 Waist、Neck 声明为基本的 real 类型。 分清用户自定义数据类型与这个类型的一个实际项之间的区别非常重要。后者称为数据类型 的一个实例 ( instance )。 一 个用户自定义的数据类型,其本质是一个用来构建数据类型实例的模 板。该模板描述了这种类型的所有实例所具有的属性,但是它本身并非这种类型的一个实例(这 就好比,饼干模是做饼干的模板,但其本身不是饼干)。在上面的例子中,用户自定义的数据类 型 EmployeeType 被用来构建了该类型的三个实例,即 DistManager 、 SalesRepl 和 SalesRep 2。 8.5.2 抽象数据类型 尽管用户自定义数据类型的概念比较有用,但它还不足以创建完整意义上的新数据类型。 一个完整的数据类型包含两部分: (1) 一个预先确定的存储系统(如整型情况中的二进制补码 系统和实型情况中的浮点系统), (2) —组预先定义的操作(如加和减)。具体来说,程序设计 语言中的基本数据类型要与其基本的操作相联系。如果程序员将一个变量声明为一个基本类型, 则无需进一步的定义,程序员就可以对该变量进行基本的操作。 然而,传统的用户自定义数据类型仅支持程序员定义新的存储系统,而没有提供对具有这 些结构的数据进行处理的操作。为了对此进行说明,这里假设要在一个程序中创建和使用几个 整数栈。其方法可以是,将每个栈实现成一个有20个整数值的同构数组。栈底中的项可以放在 (压入)数组的第一个位置,栈的其他项将相继放在(压入)数组的髙位项处(参见 8.3 节的问 题与练习7)。再用一个整型变量作为栈指针,用来存放数组项的下标,而下一个栈项将会被压入 到该数组中。因此,每个栈都由一个存放栈本身的同构数组和一个起着栈指针作用的整数组成。 为了实现这个构想,首先要用下歹1」形式的语句来建立一个称为 StackType 的用户自定义类型: define type StackType to be {int StackEntries[20]; int StackPointer = 0; } (注意,仿效如 c 、 C ++、 C # 及 Java 这些语言,就可以假设数组 StackType 的下标范围也是0〜19, 8.5 定制的数据类型 265 这样 StackPointer 指针的初始值就为0。)做了这个声明之后,我们就可以通过如下语句来声明称 为 StackOne 、 StackTwo 和 StackTh ree 的梭: StackType StackOne, StackTwo, StackThree; 此时,变量 StackOne 、 StackTwo 和 StackThree 中的每一个都可以引用一个唯一的存储单元 块,用以实现各自的栈。 但是,如果现在要把 25 这个值压入到 StackOne , 那么该怎么办?当然,我们希望能屏蔽掉以 栈的实现为基础的数组结构的细节,而仅仅将栈作为一种抽象工具来使用,即可能会用类于 push(25, StackOne) 这样的一个过程调用。但是,如果不定义称为 push 的相应过程,那么这样的一条语句就不可用。 要完成对 StackType 类型的变量的操作还包括从栈中弹出项、检查栈是否为空以及检查栈是否已 满,而这所有的操作都要求再定义相应的过程。简而言之,我们定义的 StackType 数据类型并不 包括与之关联的所有特性。 对此问题的解决办法是,对定义语句 define type 进行扩展,使其既包括数据的描述,又包 括相关的处理过程。例如,可以这样写: define type StackType to be {int StackEntries[20]; int StackPointer = 0; procedure push(value) {StackEntries[StackPointer] 一 value; StackPointer StackPointer + 1 : } procedure pop ... } 这些语句是用来表明: StackType 类型与称为 StackEntries 和 StackPointer 的变量相关,也与称为 push 和 pop 的过程相关。(为了简化,这里包括了一个非常简单的 push 过程版本。实际上,这个 过程在插入一个新项前,应该保证栈不能满。) 利用这个扩展过的 StackType 类型的定义,就可以通过 语句: StackType StackOne, StackTwo, StackThree; 将 StackOne 、 StackTwo 和 StackThree 声明为枝。然后,通过诸如下面的 语句: StackOne.push(2 5); 把项压入这些栈中。该语句表示用值25作为实际参数,进而执行与 StackOne 相关联的 push 过程。 这里,将包含了操作定义的用户自定义数据类型称为 抽象数据类型 (abstract data type )。 所 以,相对于那些更为基本的用户自定义数据类型而言,抽象数据类型就是完整的数据类型。在 20世纪80年代,抽象数据类型出现在 Ada 等语言中,这就代表了在编程语言设计方面前进了一大 步。今天,面向对象语言提供了称为类的抽象数据类型的扩展版本,在下一节中将会介绍到。 问蹰与练习 J - - rr- 1. 数据类型与该數 _ 製的一个实例之间有什么不同厂 \ 2. 用户自定义数象数据类型之间有什么不同 , 3. 请描述用来实数据类型。 ^ 4. 请描述用来卖户的抽象数据类型。 266 第 8 章数据抽象 *8.6 类和对象 在第6章中我们己经讨论过,面向对象范型导致了系统可由称为对象的单元组成,而任务是 通过对象之间的交互来完成的。每个对象就是一个实体,并响应来自其他对象的消息。对象由 称为类的模板来描述。 在许多方面,这些类实际上就是抽象数据类型(它们的实例称为对象)的描述。事实上, 在许多流行的面向对象的程序设计语言中,用来定义类的语句与上节中介绍的 define type 语句 非常相似。举例来说,图 8-27 展示了,在 Java 语言和 C # 语言中,如何定义 StackOfIntegers 类。 (在 C # 语言中,等价类的定义具有相同的结构,但在语法上稍微有所不 同。) 可以注意到,这里 的类与上一节描述抽象数据类型 StackType 所用的 define type 语句之间有些类似。这里所描述的类/ 类型包括了 一个称为 StackEntries 的整型数组, 一 个用来确定数组中栈顶位置的整数 StackPointer, 以及一些用来处理榜的过程。 class StackOfIntegers {private int[] StackEntries = new int[20 ]; private int StackPointer = 0 ; public void push(int NewEntry) {if (StackPointer 0) return StackEntries[--StackPointer ]; else return 0 ; 图 8-27 Java 和 C # 语言中实现的整数栈 在 Java 或 C # 程序中,可以利用这个类作为模板,用以下语句来创建一个名为 StackOne 的 对象: StackOf Integeirs StackOne = new StackOf Integers (); 或者在 c # 程序中,用以下语句来创建该 对象: StackOf 工 ntegers StackOne (); 在以后的程序中,使用以下语句,可以将值106压入到 StackOne 钱中: StackOne.push( 106 ); 或者可以用下面的语句把 StackOne 的栈顶元素读取到变量 OldValue 中: OldValue = StackOne,pop {); 这些特征与抽象数据类型的那些特征本质上是一样的。然而,类与抽象数据类型之间还是 有些区别的。前者是后者的扩展。例如,如在 6.5 节中已经介绍过的,面向对象语言允许类从其 他的类继承属性,并包括称为构造函数的特殊方法,当创建对象时,用其来定制个性化的对象。 而且,类通常都有不同程度的封装性(参见 6.5 节),这样就可以避免其实例的内部属性受非正 常的访问。最后,类可以作为一种对相关过程进行分组的方法,因此,类可以只由过程定义组 成。从这个意义上讲,可以把类称为抽象类型,而不是抽象数据类型。 *8.7 机器语言中的指针 267 繼 ::纖織羞— . . ; 」 ' ,… 本章所讨论的数据结枸已錢,成为标准的:编程結构,事实上,、囪为共‘准性,咚直辛许多 编程环境都把它们当作原语一样对待 。 在 o +编程鉍繞中就可以我:到一个例 子:; ,即適过梯准 模板库 (Standard Template 細 y> STL) 使该球境的功鵠果为^ 虽: §TL 中&含了 一纽预;毛 定义好的类,这些类是用来描述常用的数拇结构。 因 1 此,通过在 C 拜赛序 申并入 STL 衿这种 方式,租序员就可以从描述这#结构细节的工作中解藏出来;,.他初泉愈>明所相的标识符是 什么类型就行,就像在义 6 节中将 StackCtoe 声明为 Sta . ckOfInteger 右类型那样。 最后可以得出 结论: 类和对象的概念体现了程序中数据抽象的表示技术又前进了一大步。事实 上,正是由于这种以方便的方式来定义和使用抽象的能力,才导致了面向对象设计范型的流行。 问题与练习 1. 抽象数据类型与类在哪些方面类似?在哪些方面存在着不同? 2. 类与对象有什么不同? - , 3. 请描述一个类,要求用该类作为构建整数队列类型对象的 模板。 *8.7 机器语言中的指针 本章已经介绍过指针,并介绍了如何利用指针来构建数据结构。本节我们将讨论如何在机 器语言中处理指针。 假设我们要用附录 C 中所描述的机器语言写一个程序,要求从图 8-12 所示的栈中弹出一个 项,然后将其放入到一个通用寄存器中。换句话说,就是要将存储单元中的内容加载到一个寄 存器中,而这个存储单元包含的是栈顶的项。我们的机器语言提供了两条指令用于加载寄 存器: 一条指令是用操作码2,另一条指令是用操作码1。回想一下,在操作码2的情况中,操作数字段 包含了被加载的数据,而在操作码1的情况中,操作数字段则包含了被加载数据的地址。 由于不知道内容是什么,所以用操作码2达不到预期目的。而且,不知道地址,也不能用操 作码1。毕竟,在程序执行的时候,栈顶的地址会发生变化。然而,我们知道了栈指针的地址。 也就是说,知道了所要加载数据的地址的位置。于是,我们需要的就是第3个用于加载寄存器的 操作码,在这条指令中,操作数字段包含了指向被加载的数据指针的地址。 为了实现这个目标,我们对附录 C 中的机器语言进行扩展,使其包含操作码 D 。 使用这个操 作码的指令可能具有这样的形式,即 DRXY , 这就表示将地址为 XY 的存储单元的内容加载到寄 存器 R 中(如图 8-28 所示)。所以,如果栈指针在地址 AA 的存储单元中,则指令 D 5 AA 就实现了 将栈顶的数据加载进寄存器5中。 然而,这条指令并没有完成出栈操作。我们还必须将栈指针减1,以便让它指向新的栈顶。 这也就是说,在加载指令之后,机器语言程序还必须将栈指针加载到一个寄存器,将其减去1, 然后再把结果存回到存储器。 如果不用存储单元,而用某个寄存器来作为栈指针,那么就可以减少栈指针在寄存器与存 储器间的来回移动。但是,这也就意味着必须重新设计加载指令,这样它就要求指针在寄存器 中,而不是在主存中。这样一来,我们不使用早些时候那个方案,而是用操作码 D 定义一条指 令,令其具有 DR 0 S 的形式,这就表示将寄存器 S 所指的存储单元的内容加载到寄存器 R (如图 8-29 所示)。于是,一个完整的出栈操作就可以这样来 完成: 在这条指令之后添加一条指令(或 268 第 8 章数据抽象 主存储器 i :; i! l , !ui!j' : l E : ! : | ; 'V : l|!l!l|l! . ‘ 」 期的.执 I, __ I I r L . L , , - ' - 、、 . 存耱的 - I ... ... ••:••••• •'•:•: ■;:■■■..■ . - ; : M :;:■ -.::::,. : :: : ... . . 1. 在一个制造厂中确定两个部门,说明 它们对 同一个或者类似的库存信息会有不同的用途。然后,说 明两个部 £] 的子猶式如何 不同。 2•,数 锯库模型的目标是什么? _ 9.2 关系模型 279 l 〜 a ^ •蹄别 ㈣ ;漱土融•税添 嫩祝& 疏故拟 i . 本节将更详细地讨论关系数据库模型,它描绘的是用矩形表格存放的数据,这种表格称为 关系 ( relation ), 这类似于电子制表程序显示信息的格式。例如,在关系模型中,一'个公司员工 • 的信息就表示为如图 9-3 所 7」 k 的关系。 Empl Id _ Name __ Address _SSN v Joe^fe 6aker- - 25X15 二 : '翁 Kfo 碗賴 ,” i 1112333^3 34Y70 Cheryl H. Clark 563 Downtown Ave. ._9990t)9999 23Y34 G. Jerry Smith 1555 Circle Dr. 111005555 • • # • • • • • 參 • • • ••••••• 7r : ill I _ . . I ' •••• - -.. 9.2 关系模型 图 9-3 包含员工信息的一个关系 关系中的一行称为一 个元组 ( tuple ) (有人读作 “ TOO - pul ”, 也有人读作 “ TU - pul ”)。 在如 图 9-3 所示的关系中,元组由某个特定员工的信息组成。因为每列描述的是对应的元组所表示的 实体的一些特征或属性,所以关系中的列称为属性 ( attribute )。 9.2.1 关系设计中的问题 设计关系数据库的关键步骤是设计构成这个数据库的关系。尽管这个工作看上去很简单, 但对于粗心的设计者来说,仍有不少难以捉摸的陷阱。 假定除了如图 9-3 所示关系中所包含的那些信息之外,我们还想要添加员工工作的信息。这 里需要为每个员工添加一个工作经历,包括如下一些 属性: 如职务(秘书、办公室经理、楼层主 管)、职务代码(每种职务的职务代码是唯一的)、与该职务有关的技能代码、该职务所在部门, 以及该员工任职的开始日期和终止日期(如果员工仍任现职,则终止日期用*号表示)等。 解决这个问题的一种方法就是扩展如图 9-3 所示的关系,在表格中加进这些属性列,如图 9-4 所示。然而,仔细检查这个结果会发现一些问题。问题之一是,信息的冗余导致了效率低下。 这个关系中不再是每个员工对应一个元组,而是每次职务指派就对应一个元组。如果一个员工 在公司里历任好几个职务,那么新关系中的几个元组就会包含该员工的相同信息(姓名、地址、 员工代号及社会保险号)。例如,因为 Baker 和 Smith 担任过多个职务,所以有关他们的个人信息 就会有重复。还有,当某个特定的职务由几个员工担任过,那么与此职务相关的部门及相应的 技能代码也会在表示职务的每个元组中重复。例如,因为楼层经理由多个员工担任过,所以这 个职务的描述就会重复。 Empl Id Name Address SSN Job Id Job Title Skill Code Dept Start Date Term Date 25X15 Joe E r Baker 33 Nowhere St. 111223333 F5 Floor manager FM3 Sal^s 9-1-2009 9-30-2010 : 25 : )U^ r . Joe E. Baker :: 33 : N : 6^here St ' ■1112^3333 ! D7 Dept. head K2 Sales 10-1-2010 * 34Y70 Cheryl H. Clark 563 Downtown Ave. 999009999 F5 Floor manager FM3 Sales 10-1-2009 * 23Y34 G. Jerry Smith 1555 Circle Dr. 111005555 S25X Secretary T5 Personnel 3-1-1999 4-30-2010 23Y34 G. Jerry Smith 1555 Circle Dr. 1T1005555 S26Z Secretary T6 Accounting 5-1-2010 • * • * * • • • • • • • • # • • • • • • • * • 舉 • 拳 • • * • 图 9-4 包含冗余的关系 280 第 9 章数据库系统 对于这样一种扩展的关系,如果考虑从数据库中删除信息的话,会导致另外一个更为严重 的问题。例如,假定只有 Joe E . Baker 是唯—— 个拥有 D 7 这个职务代码的员工,如果他离开公司 了,并从图 9-4 表示的数据库中删除,那么,有关 D 7 的职务信息就会丢失,因为包含 D 7 职务需 要 K 2 技能等级这个事实的元組只有与 Joe E . Baker 有关的那个元组。 — 玀!闘 , 机細蠤旋 ■:( 典鳞举姆龛杂,申 r : 泛豫用 v 」: 巍鍵獅 放荃翁輪:,:臟難賴 慧釦樣 ,:獅薇^難纖麵植; 打印和拂序轉參的雜錐,::辦遥 f ■灰:#类淖电.,総 鳞辣来 _豪_馨 :( 賴虞 , J L 唪 •土 银是被辆秦摹—::铜知舉教♦苟的“聯鎮義家2•篇德邊:备 — 声秀的:矣'系聲搌库 V :考減涵_承薄声伴。 '如& 戎赛|摄_奶聲爾槪幸:- 1 场 ; 上派行产'品的:^氣 「為 破娜南.漏 Ep 截:才;好: ■的备 轉 V 你也许会认为,能做到只删除元组中一部分信息,就可以解决这个问题,但是这又会引起 新的麻烦。比如,: F 5 职务的信息是留存在一个部分的元组中,还是留存在关系中其他什么地方? 而且,这种利用部分元组的想法正好说明了该数据库的设计还能够进一步改进。 所有这些问题产生的原因就在于我们在一个单一的关系里融进了多个概念。图 9-4 中的扩展关 系包含了员工的直接信息(姓名、员工代号、地址、社会保险号),有关公司现有职务的信息(职 务代号、职务、部门、技能代号),以及有关员工和职务间关系的信息(开始日期、终止日期)。 基于以上的分析,我们可以用这样的一种方式来解决问题,即用3个关系来重新设计这一系统, 每个关系对应前面的一类信息。我们可以保留图 9-3 中所示的那个原始关系(现在我们称它为 EMPLOYEE 关系),再插入称为 JOB 和 ASSIGNMENT 的两个新关系,就产生了如图 9-5 所示的数据库。 EMPLOYEE 关系 Empl Id Name Address SSN 1 25X15 23Y34 ^ t 潮鞠辦 ii 秘郝 11 侧節 5S JOB 关系 Job Id JobTitle Skill Code Dept ’I 諡: iiWii :1 '" .物3、, ASafe]f 拳 _ ASSIGNMENT 关系 Empl Id Job Id Start Date Term Date 23Y34 賴 :. W :卜 , : ' 樓 — r,^Z^7^i r t r :Vt. V:v tv〆 1 1 VvlH 」 T #t I ^ , hi 1 l-H » r 23Y34 '^'262 " ■ 1 r _ H * J , - 「 L rH ,- III hH n L 1:*. J | • * • • • _ • _ 图 9-5 由三个关系组成的员工数据库 9.2 关系模型 281 这样数据库就由3个关系组成,即 EMPLOYEE 关系包含员工的信息, JOB 关系包含职务的信 息, ASSIGNMENT 关系包含职务经历的信息。其他的信息则隐含在不同关系信息的组合中。例 如,如果知道一个员工的代号(也就是员工的 ID 号),就可以先用 ASSIGNMENT 关系找到该员工 任职过的所有职务,再用 JOB 关系找到与这些职务有关的部门(如图 9-6 所示),这样就可以找到 这个员工任职过的部门。通过这样一些步骤,任何原先可以从单一的大型关系里面获得的信息, 现在都能从3个较小的关系中获得,并且不会出现前面提到的那些问题。 EMPLOYEE 关系 Empl Id Name Address SSN 25X15 Joe E. Baker 33 Nowhere St. 111223333 34Y70 Cheryl H. Clark 563 Downtown Ave. 999009999 23Y34 G. Jerry Smith 1555 Circle Dr. 111005555 • • • • • • • • # 攀 • • JOB 关系 员工 23Y3 今 的职务 ' Job Id S25X S26Z F5 • Empl Id JobTitle Skill Code Secretary Secretary Floor manager 争 参 T5 T6 FM3 • 參 ASSIGNMENT 关系 Job Id Start Date Dept Personnel 1 t Accounting Sales —i 鲁 Term Date 包含在 人事部 和财务部 -[ 23V34 . . :S : 念较 3-1-1999 4-30-2010 r 34Y70 F5 10-1-2009 -M- 23V34 S26Z 5-1-2010 • • • • • • • • • • • • 图 9-6 查找员工 23Y34 工作过的部门 但是,把信息划分到不同的关系中,并不总是像上面提到的例子那样顺利。例如,比较图 9-7 中的原始关系和建议分解成两个关系的 Emplld (员工代号)、 JobTitle (职务)及 Dept (部 门)3个属性。乍看起来,双关系系统与单关系系统好像包含相同的信息,但事实并非如此。比 如,要查找某员工工作过的部门,这在单关系系统中很容易,只需查找包含该员工代号的那个 元组,取出相应的部门即可。然而,在双关系系统中,所要的信息未必存在。我们可以找到该 员工的职务及具有这个职务的一个部门,但这并不一定意味着该员工就在这个部门工作,因为 几个部门可以有同样的职务。 于是,我们可以看出,把一个关系分解成几个比较小的关系时,信息有时会丢失,有时不 会丢失,后者称为无 损分解 (lossless decomposition , 或 nonloss decomposition )。 对这种关系特 性的研究是重要的设计依据,其目标就是找出会在数据库设计中引起问题的一些关系特性,并 找到重新组织那些关系的方法来消除这些出问题的特性。 Empl Id JobTitle Dept 图 9-7 关系和提议的分解 9.2.2 关系运算 我们对数据是如何按照关系模型来组织的有了基本的了解以后,接下来的工作就看看如何 从由关系组成的数据库中提取信息。我们可以先考察要对关系执行的某些操作。 我们常常会从一个关系中选取某些元组。比如,要检索某个员工的信息,就必须从 EMPLOYEE 关系中选取包含相应“员工代号”属性值的元组,或者为了得到某一部门的所有 职务,就必须从 JOB 关系中选取具有该部门属性的元组。这样选取的结果是,从父关系中选 取的元组构成了另一个关系。选择某一特定员工信息的结果是产生了一个关系,该关系只包 含从 EMPLOYEE 关系获得的一个元组。而选择与某个部门相关的元组,会生成一个关系,其 中包含来自 JOB 关系的几个元组。 简而言之,在一个关系上想要执行的一种运算就是要选取具有某些特性的元组,并把这些 选出的元组放到一个新的关系中。为了表示这种运算,我们釆用下面的 语法: NEW 一 SELECT from EMPLOYEE where Emplld = "34Y70 M 此语句的语 义是: 创建一个名为 NEW 的新关系,它包含从 EMPLOYEE 关系选得的其 Emplld 属性等于 34 Y70 的那些元组(本例中应该只有一个元组)(如图 9-8 所示)。 9.2 关系模型 283 到一个叫 NEW 1 的新关系中。我们查找的清单是这个新关系里的 JobTitle 列。: PROJECT 运算就 是提取这个列(或者必要时是几个列),并把结果放到一个新关系中。这个运算表示为 NEW2 一 PROJECT JobTitle from NEWl 其结果是创建另一个新关系(名为 NEW 2 ), 它包含从 NEW 1 关系中 JobTitle 列得到的那些值所 构成的一个列。 作为 PROJECT 运算的另一个例子,语句 MAIL — PROJECT Name , Address from EMPLOYEE 可以用来获取所有员工的姓名和地址的清单。这个清单是新创建的(有两列的)关系,名为 mail (如图 9-9 所示)。 EMPLOYEE 关系 Empl Id Name Address SSN 25X15 Joe E. Baker 33 Nowhere St. 111223333 24Y70 Cheryl H. Clark 563 Downtown Ave. 999009999 23Y34 G. Jerry Smith 1555 Circle Dr. 111005555 春 • • 導 • • 拳 ■ • 搴 • _ MAIL - PRQ1ECT Name, Address from EMPLOYEE mail 关系 ▼ Name Address Joe E. Baker Cheryl H. Clark G. Jerry Smith 33 Nowhere St. 563 Downtown Ave. 1555 Circle Dr. 图 9-9 PROJECT 运算 另外的一个用于连接关系数据库的运算是 JOIN 运算,它用来把原来不同的关系组合成一个 关系。两个关系结合产生一个新关系,而新关系的属性则由原来两个关系的属性组成(如图940 所示)。这些属性的名称与原先关系中的名称一样,只是每个都加上了原关系作为前缀(如果包 含属性 V 和 W 的关系 A 与包含属性 X 、 Y 及 Z 的关系 B 相结合,那么结果就有名为 A . V 、 A . W、B . X 、 B . Y 和 B . Z 的5个属性)。这种命名约定保证了新关系的属性只有唯一的名称,即使原先的几个关 系中有相同的属性名称也没关系。 新关系的元组(行)由原来两个原始关系的元组串接而成(再见图9-10)。哪几个元组会连 接成新关系的元组取决于连接 ( JOIN ) 的条件。一个条件就是指定的属性要有相同值。事实上, 图 9-10 表示的就是这种情况,它演示了执行语句 C — JOIN A and B where A. W = B. X 的结果。在这个例子里,关系 A 的一个元组与关系 B 的一个元组串接,其条件正是两个元组的属 性 W 和 X 值相等。因此,关系 A 的元组 ( r ,2) 与关系 B 的元组 (2, m , q ) 的串接出现在结果中,因 为第一个元组中的 W 属性的值等于第二个元组中 X 属性的值。另一方面,在最后的关系中并没有 关系 A 的元组 ( r ,2) 与关系 B 的元组 (5, g , p ) 串接的结果,这是因为这些元组在属性 W 和 X 中没 有相同的值。 284 第 9 章数据库系统 关系 A X Y Z V W 5 9 P ::.哪 ㈢ :.: 4 d e t 4 关系 B ■-.%:, ■ .; ::.:■ ::糸; q P 6 4 t f I 关系 C A.V A.W B.X B.Y B.Z 嫌. ' I :爭: q t 4 4 d e l 4 4 t f 图 9- 10 JOIN 运算 看另一个例子,图 9-11 所示为执行语句 C 一 JOIN A and B where A. W 说明事备到达了它的提交点与没到达提交点的区别是什么。 : DBM 沒是怎样防止太量的壤联回滚的?, ,3. 假定一个账户的初始佘额是400美元。有两个事务,一个事务从这个账户中支取100美元 * 另一个事 务也从同一账户中支取2加美元。,请说明,这两个不加控制的交叉事务怎样才能使账户的最终余额为 100美充、200美元和300美元 。 4. a . 筒述事务对数据库中的数据琐请求共享 访问的 可能结果。 b. 简述事务对数据库中滴数据项请求互斥访问的可能 结果。 1 5. 试描述会导致执行数锯库操作的事务间出现死锁的一系列事件 u 6. 请说明怎样打破第5 题中的 .死锁情况。你的解决办法是否要用到数据库管理系统中的日志文件?请解 释你的:答案。 ‘ 1 *9.5 传统的文件结构 本节我们拋开多维数据库系统的研究来讨论传统的文件结构。这些结构代表了数据存储和 检索系统的历史开端,现在的数据库技术就是由此发展而来的。为这些结构开发的许多技术(如 索引技术和散列技术等)是构建今天大规模、复杂数据库的重要工具。 9.5.1 顺序文件 顺序文件 (sequential file ) 是这样的一种文件,即它从头到尾都是以顺序的方式进行访问 的,好像文件中的信息都排成一行。这种文件的例子有音频文件、视频文件、包含程序的文件 和包含文本文档的文件等。事实上,大多数由个人计算机用户创建的文件都是顺序文件。例如, 当保存一个电子表格时,它的信息就会作为一个顺序文件进行编码和保存,电子表格应用软件 能够重新构建电子表格。 文本文件是顺序文件,它的每个逻辑记录是用 ASCII 码或 Unicode 码编码而成的单个 符号。 文本文件常常作为一种基本的工具,用来构建诸如员工记录文件这些更为复杂的顺序文件。为 此,只需建立一个统一格式,把每个员工的信息表示为一串文本,然后按照格式对这些信息进 行编码,接下来就把这些员工记录一个接一个地记录成一个文本串。例如,可以构建这样的一 个简单的员工文件,即每个员工记录为可以输入31个字符的字符串,其中25个字符作为一段, 用来表示员工的姓名(每段中多余处用空格填充),随后6个字符作为一段,用来表示员工的工 号。最终的文件将会是一个很长的编码过的字符串,其中每31个字符组成的字符块代表了一个 292 第 9 章数据库系统 员工的信息(如图 9-14 所示)。从文件中,我们可以根据由31个字符的信息块所组成的逻辑记录 来实现信息检索,每个块中的各个字段是根据构成块的统一格式来识别的。 文件由一串块组成, 每个块含有31个字符 员工姓名 员工工号 图 9-14 以文本文件实现的一个简单的员工文件结构 顺序文件中的数据在大容量存储器里存放时必须要保持文件的顺序特性。如果大容量存储 系统本身具有顺序性(如磁带和 CD ), 那么就可以直接做到这一点。在此,我们只需根据存储 介质的顺序特性,将文件记录到存储介质中。然后,处理文件的过程仅 仅是: 按照文件内容建 立的顺序来读取和处理 它们。 播放音乐 CD 就是这么一个过程,因为音乐作为一个顺序文件,沿 着螺旋型轨道,一个扇区接着一个扇区进行存放。 然而,在磁盘存储的情况下,文件将分散在不同的扇区中,因而会以各种顺序来读取。为 了保持正确的顺序,大多数操作系统(更准确地说是文件管理程序)都会维护一张存放文件的 扇区列表。这个表,作为磁盘目录系统的一部分与文件记录在同一磁盘上。即使文件实际上分 散存放在磁盘的不同部分,但是利用这个表,操作系统就能以正确的顺序检索扇区,就好像文 件真的是按顺序存放一样。 顺序文件处理中的一个固有问题就是必须要检测何时到达文件的末尾。通常我们把顺序文 件的末尾称为 EOF ( End - Of - File , 文件结束)。有许多方法可以用来标识 EOF , 一种方法是在文 件的末尾放置一个专用的标记,称为哨兵 ( sentinel )。 另一种方法是利用操作系统的目录系统 中的信息来确定一个文件的 EOF 。 也就是说,由于操作系统知道哪个扇区包含有此文件,它也 就知道这个文件在什么地方结束。 一个小公司的工资单处理就是使用顺序文件的一个典型例子。这里我们可以想象出一个顺序 文件,它是由一系列的逻辑记录组成,每条记录都包含一个员工的薪水信息(如姓名、员工工号、 工资等级等)。依据这些信息就能定期打印出支票,每读取一个员工的记录,就能计算出该员工 的工资,然后再打出相对应的支票。处理这样一个顺序文件的操作,可由以下语句做 示例: while (未到达 EOF ) do (从该文件中提取下一条记录并处理它) 当顺序文件中的逻辑记录用键字段来标识时,文件通常就可以这样安排,即按照由键(可能 是字母键或者是数字键)确定的顺序来安排文件中的记录。这样一种安排简化了文件信息的处理 工作。例如,假定处理工资时,必须要求依据考勤单的信息更新每个员工的记录。如果包含考勤 单记录的文件和包含员工记录的文件都根据同一个键按照同样的次序存放,那么,就能顺序地访 问两个文件来进行更新处理,即用从一个文件读取的考勤单来更新另一个文件的相应记录。这是 一 个重大改进,因为如果文件不按照相应次序来存放,就必须反复地查找,而上述方法就克服了 *9.5 传统的文件结构 293 这个缺点。所以,更新典型的顺序文件通常需要多个步骤进行处理。 首先, 新信息(例如考勤单 中的信息)记录在一个称为事务文件的顺序文件中,这个事务文件按照要被更新的文件(称主文 件)的次序进行排序,然后,通过从两个文件中顺序地读取记录来对主文件记录进行更新。 与这种更新过程稍有不同的是归并过程,即把两个顺序文件合并成一个包含原来两个文件 记录的新文件。假定两个输入文件的记录是依据一个公共的键字段按照升序来排列的,并假定 归并产生的输出文件也是按键的升序来排列。图 9-15 概述了这个典型的归并算法。其基本思想 是顺序地扫描两个输入文件,从而构建出输出文件(如图 9-16 所示)。 procedure MergeFiles (InputFileA , InputFMeB . OutputFiie ) if (两个输入文件都处于 EOF) then (停止, OutputFile 为空) if ( InputFileA , 不在 EOF) then (声明它的第一个记录为当前记录) if ( InputFMeB , 不在 EOF) then (声明它的第一个记录为当前记录) while (两个输入文件都不在 EOF) do (将键字段值较小的当前记录放在 Outputnie 中; if (该当前记录是其对应输入文件的最后一个记录) then (声明该输入文件在 EOF) else (声明该输入文件中的下一个记录是该文件的当前记录) ) 从不在 EOF 的输入文件的当前记录开始 复制其余记录到 OutputFile 图 9-15 归并两个顺序文件的过程 输出文件 ^ S 入文件~ : D I ; A C F : ABC . ; B D D i \ . . ! A C 图 9-16 A B C D E F 归并算法的应用(字母用于代表整条记录,具体字母表示记录的键字段的值) 294 第 9 章数据库系统 9.5.2 索引文件 对于数据处理的次序就是其文件存储次序的情况,顺序文件是存储这类数据的理想选择。 然而,当文件必须以一种不可预测的次序进行检索时,那么这种文件的效率就不高了。在这种 情况下,需要一种方法来快速确定所需逻辑记录的位置。 一 种流行的方法就是使用文件索引, 这种方式与书本里的索引用来定位主题在书中位置的方式非常一致。这种文件系统称为索引文 件 (indexed file ) 。 文件的索引包含存放在该文件中的键的列表和指示包含每个键的记录存放位置的项。这样一 来,为了要找到某个记录,首先需要在索引中找到指定的键,然后再读取存放在与该键相关位置处 的信息块。 文件的索引通常作为一个单独的文件与被索引的文件存放在同一个大容量存储设备里。在 文件处理开始之前,通常要先将索引调入主存储器中,这样一来,当需要访问文件中的记录时, 就会很容易地找到该记录 C 如图 9-17 所示)。 当索引文件被打 主存储器 幵时,索引被传 大容量存储器 图 9-17 打开索引文件 在维护员工记录时就有索引文件的一个典型例子。当想检索一个员工的记录时,如果使用 索引就可以避免冗长的查找操作。具体来说,如果员工记录文件用员工工号进行索引,那么只 要知道员工的工号,就能很快查到该员工的记录。另一个例子是音乐 CD 的播放,如果利用索引 就能较快地访问到各首乐曲。 多年以来,在基本索引概念的基础上,已经使用了许多不同的索引技术。构建索引的一种 方式是运用层次化的方式,以便索引呈现出分层结构或树结构。最突出的例子就是大多数操作 系统为组织文件存储所采用的分层目录系统。在这种情况下,目录(即文件夹)起到索引的作 用,而每个索引又包含了指向其子索引的链接。从这个角度来看,整个文件系统只是一个大型 的索引文件。 9.5.3 散列文件 尽管索引技术为访问数据存储结构中的数据项提供了一种较快的访问机制,但维护索引的 开销也比较大。散列 ( hashing ) 技术也能提供类似的访问效果,但无须那样大的开销。与索引 系统的情况一样,散列技术也是利用键值来定位记录。但是散列技术并不是从索引中查找键, 而是直接通过键确定记录的位置。 散列系统可以概括 如下: 数据存储空间被分成几个区,称为存储桶 ( bucket ), 每个桶能放 几条记录。根据将键的值转换为桶号的算法,可以将记录分散存放于这些桶中。这里,将键的 值转换为桶号的算法称为散列函数 (hash fimction )。 每条记录就存放在通过这样处理确定的桶 里。因此,要检索一条已经置于这种存储结构中的记录,首先要对该记录的标识键应用散列函 *9.5 传统的文件结构 295 数,以确定相应的桶,然后检索桶中内容,最后从检索的数据中查找所需要的记录。 散列不仅能用于从大容量存储器中检索数据,也是从存放在主存的大数据块中检索数据项 的一种方法。当散列用在大容量存储器中的存储结构时,其结果称为散 列文件 (hash file )。 当 散列用在主存中的存储结构时,其结果通常称为 散列表 C hash table )□ 散列法不只是用来作为构建高效数据存储系统的一种手段、例如,散列法还可以用作认 证因特网上传送的消息的一种方法。其基本思 想是: 以秘密方武对消羼进行散列运算,然后 将得到的值与消息一起传送。为了认证消息,,接收方續收到妗消奉療行教列处理 ,( 以同# 的 消息发生改变的可能性很小)。如果得到的值与原来的值不一致,就认为该消息已被破坏 & 那 些对此感兴趣的人可能希望从因#网上搜寻到有关: M 雜的信息、其实® )5 是在&证庄用领域 有着广泛应用的散列爾数。 ' 辦_^测技术可以,看作散列法在认证领域的 一种在 用,, 其寒 0经是一目了潍的事了。例: 如,校验位的捷用本质上就是一冬散 L 先,在此系统中,位 模式只 教列为0和1,然后将这 个值与初始位模式一起传送。如果最终接收的位模式不能散列成同样的值,那么就认为这个 位模式 : 已被破•坏。 现在我们把散列技术运用在典型的员工文件中,这里,每条记录包含的是公司中一个员工 的信息。首先,在海量存储器中创建几个可用的区域,用来实现桶的功能。至于如何设计决定 桶的数目和每个桶的大小,稍后再讨论。现在,我们假定已创建了41个桶,桶号从0—直到40。 (我们选择41个桶,而不是偶数40个桶,原因稍后再解释。) 现在我们假设用员工工号来作为识别员工记录的键。这样,下一步工作就是设计一个散列 函数,把这些键转换成桶号。虽然员工工号可能是 25 X 3 Z 或 J 2 X 35 这样的格式,不是数字型的, 但它们是以位模式存储的,我们能够将这些位模式解释成数字,利用这个数字解释,就能让任 何一个键去除以可用的桶号,然后记下余数。在这个例子中,佘数将是 0-40 的一个整数。因此, 我们就可以用每次做除法得到的余数来确定41个桶中的一个(如图 9-18 所示)。 2 SX 3 Z 卜 :」”:: lf ii 乂 V, 1 ):. doiiODioopittjioio 5 QmtionoloiiGiA 增制__.(: 215$43[337 562 1 被縣_齡数. ; 、 3 桶參 3 图 9-18 将键字段值 25 X 3 Z 散列到41个桶中的一个 以此作为我们的散列函数,接下来再分别考虑每条记录,继续构建文件。通过使用散列函 数对其键除以41得到一个桶号,然后再把该记录存放在这个桶中(如图 9-19 所示)。以后,如果 当我们需要检索一条记录时,只需将这个散列函数应用到该记录的键,以确定相应的桶号,然 296 第 9 章数据库系统 后就可以从这个桶里查找所要的记录。 41 J55 . .':谢 2 41 ?96 41 )14 14 余数 当除以41时,键字段值14,55,96都得 到余数14,所以这些记录存放在桶14中 95 136 i r - ’ 濟織 '4 : WS 343 -海量存储器中的桶 #13 #14 #15 #16 图 9-19 散列系统的基本原理 现在,让我们来重新考虑一下把存储区分成41个桶的问题。首先注意,要想得到一个有效 的散列系统,要存放的记录应当均匀地分布在这些桶中。如果发生一个不成比例的键数目恰巧 散列到同一个桶里——这种现象被称为群集 ( clustering ) ——那么,在一个桶里就会存入不成 比例数目的记录。结果是,从这个桶里检索一条记录就会花费更多的查询时间,这也就失去了 散列技术的优势。 现在再来看,如果我们选择把存储区域分成40个桶,而不是41个桶,那么散列函数涉及的 除数(即除键的值)就是40,而不是41。但是,如果被除数和除数有一个公因子,而这个公因 子也会出现在余数中。具体来说,如果存储在散列文件中的数据项的键碰巧都是5的倍数(也是 40的约数),那么当用40来除时,5这个因子就会出现在余数中,并且数据项就会群集到与余数0、 5、10、15、20、25、30及35相对应的那些桶里。类似的情况还会出现在键是2、4、8、10及20 的倍数的情形中,因为它们也都是40的约数 D 因此,我们选择把存储区域分成41个桶,因为41 是素数,选择它,就可以消除公约数,从而减少群集的可能性。 但是,群集的可能性绝对不能完全消除,即使用的是精心设计的散列函数,在文件构建 过程的早期,还是非常有可能存在两个键经过散列后,得到同一个值的情况。这个现象被称为 碰撞 ( collision ). 为了理解其中的原因,考虑下面的情况。 假设我们已经建立了一个在41个桶中随机分配记录的散列函数,这时候的存储系统是空的, 并且准备一次插入一条记录。当插入第一条记录时,它将会被放进一个空桶里。然而,当插入第 二条记录时,41个桶中还有40个桶是空的,这样一来,第二条记录被放进空桶的概率只有40/41。 假设第二条记录被放进了一个空桶,那么当放第三条记录时就只能找到39个空桶,所以,被放 进空桶的概率是39/41。继续这个过程,就可以发现,如果前7条记录都被放进了空桶,那么第 八条记录被放进余下空桶的概率就只有34 / 41。 基于以上的分析,我们就能计算出所有前8条记录都被放进空桶的概率,即每条记录被放入 空桶概率的乘积,这里假设前面的记录都已被放入空桶。那么这个概率为 (41 / 41) (40 / 41) (39 / 41) (38 / 41) ... (34 / 41)=0.482 问题是这个结果小于一半。也就是说,当在41个桶里分配记录时,很可能在存放第八条记录的 时候,就会发生碰撞现象。 9.6 数据挖掘 297 发生碰撞的高概率表明,不管如何精心选择散列函数,设计任何一个散列系统时都必须要 考虑到群集现象。特别是,一个桶有可能会装满或者溢出。这种问题的一种解决方法就是,允 许扩展桶的大小。另一种解决方法是,允许桶溢出到一个专门为解决这种问题而保留的溢出区。 无论如何,群集情况和溢出情况的出现都将使散列文件的性能明显降低。 研究表明,作为一般规律,只要记录的数目与文件中总的记录容量之比——将这个比率称 为负载因子 (load factor ) - ~•保持在50%以下,那么散列文件就会表现出良好的性能。但是, 如果负载因子攀升至超过75%,那么系统的性能通常就会降低(严重的群集现象会造成有些桶 装满或者可能溢出)。由于这个原因,如果散列存储系统的负载因子接近75%这个值,那么它通 常会以一个更大的容量进行重建。最后得出结论,通过实现散列系统来获得记录检索的高效率 是需要花费一定代价的。 ____s 議議 h 餘振所示韵 知算法 € 读,个输入文件包*键字肆值等争 BpE 的氟而另一个输入文件 归并算法是一种鞒为戲為每序购 流彳 鞠纟序算法雜核心1::你能胥輿明这种算法?,(挺示 r 任何 非空的 假墙2乎巍 #(1 春锗索麻东# 餘法敏 但是 feA 只用& ^储補 4对以下各键 ::值,: II 定相应运赛顧^放进_个輝。:螽 &»♦: 么 ii : 癍|? * 什么 i _| ’ | : v " ' : 1 @ S !: S | 議:纖::!藤:; :議; 鬚 F ___晒_|_議_議 ,7…编扭親两个1的每拉翻導^ : 时51_極,:,至杏需要多少人?:试—逸个问题^本节 | 内 容有何 美系? 报挪微蝴 KiX^e 嫩 9.6 数据挖掘 一 个迅速发展的并与数据库技术紧密相关的学科就是数据挖掘,它包括了在数据集上发现 模式的技术。数据挖掘已经成为许多领域的重要工具,包括市场营销、库存管理、质量控制、 借贷风险管理、欺诈检测和投资分析等。数据挖掘技术甚至可以运用于那些似乎不大可能会用 到的场合,例如用于确定某些以 DNA 分子进行编码的基因功能以及描述有机组织的特性。 数据挖掘活动与传统的数据库查询不同,原因在于数据挖掘所做的工作是设法确定以前未 知的模式,而传统数据库需要做的只是检索已经存储好了的事实。此外,数据挖掘操作的是静 态的数据集合,称为数 据仓库 ( datawarehouse ), 而不是经常要更新的“联机”运行的数据库^ 这些仓库往往是数据库或数据库集的“快照”。因为静态系统中寻找模式要比动态系统中简单, 所以用它们来替代实际运行的数据库。 还需要注意的是,数据挖掘的主题不单局限于计算领域,而且还涉及统计学领域。事实上, 很多人认为,由于数据挖掘源自于试图对大量不同的数据集进行统计分析,因而它更像是统计 学的一种应用,而并非计算机科学的一个领域。 数据挖掘有两种常见的形式: 类型描述 (class description ) 和类型识别 (class discrimination) D 类型描述用来找出描绘一组数据项的属性,而类型识别用来找出区分两组数据项的属性。例如, 类型描述技术可以用来发现购买经济型轿车的人的特点,而类型识别技术可以用来发现能区分 298 第 9 章数据库系统 买二手车与买新车的顾客的特性。 4 ■ T- ■ ij"" "Jx :__•::: l;:"f 54C-: .T. err j..^T-_^*- ^—=^2. §jm _y^.£ F -; T 心二 J::- I:;,;::?:- ■A;: “ ‘ ^ T dp — T - -- ^ - ^ ■- 数振库技术和数据挖掘技术的进步方纩展了生物学家在涉及模式识到余有机化合物分类 研究领域寸使用的工具。结茱就产生了:生特学的 一 个新领域,称为生輪信惠学。现在的生物 信息学源:于对 DNA 解码的研究工作, 您包括 了如蛋白质分类和理解蛋白质 _ 相互作用序列(称 为生物化学路径)的这样一些研究 I 峯然递常认为生物信息学是生物学 的…个 部分,但它很 好地 例证了 甘算机科学是如何影响甚蓋扎根于姜他领域的。 另一种数据挖掘的形式是聚类分析 (cluster analysis ), 它用来以发现类型。注意,这与类 型描述不同,类型描述是用来发现己经确定的类型中成员的属性。更明确地说,聚类分析试图 找到那些能引导发现组群的数据项的特性。例如,在分析观看某部电影的观众年龄信息的过程 中,通过聚类分析可能会发现,观众会分成两个年龄组, S 卩4〜10岁一组和25〜40岁一组。(也 许这影片吸引了孩子和他们的父母?) 还有一种数据挖掘的形式,称为关联分析 (association analysis >,它的工作是寻找数据组之 间的联系。要找到既买土豆片又买啤酒和饮料的顾客,或者在正常的工作日购物又能享受退休 优惠的顾客,正好可以使用关联分析。 孤立点分析 (outlier analysis ) 是数据挖掘的另一种形式,它试图识别出不符合规则的数据 项。孤立点分析可以用于确定数据集中的错误,它还可以检测信用卡,如果发现信用卡突然偏 离客户的正常消费模式,那么就可以确定该信用卡被盗用,甚至可以通过发现反常的行为识别 出潜在的恐怖分子。 最后,还有一种数据挖掘形式,称为序列模式分析 (sequential pattern analysis ), 它试图确 定随时间变化的行为模式。例如,序列模式分析可以揭示股票市场等经济系统中的趋势,或者 气候环境等环境系统中的趋势。 最后这个例子表明,数据挖掘的结果可以用来预测未来的行为。如果一个数据项具有表征 某个类型的属性,那么这个数据项就可能表现为这个类型的成员。然而,许多数据挖掘项目只 是旨在获得对数据的更好理解,如利用数据挖掘来解开 DNA 之谜。无论如何,数据挖掘具有巨 大的潜在应用领域,并且有望成为未来一个活跃的研究领域。 注意,数据库技术和数据挖掘之间的关系就像堂兄弟一样, 一 个领域的研究成果在另一个 领域也会有反映。数据库技术广泛运用,使得数据仓库具有以多维数据集 (data cube , 从多角 度看待数据,用 “ cube ” 这个术语来表示多维的概念)形式表示数据的能力,这就使得数据挖 掘成为可能。反过来,当数据挖掘方面的研究人员提高了实现多维数据集的技术时,这些成果 也给数据库设计领域带来了好处。 最后,我们应当认识到,成功的数据挖掘远不止包括数据集范围内的模式识别。明智的判 断还要确定哪些模式是有实际意义的,哪些只是偶然的。某个便利店卖出了大量彩票这样一个 事实对于计划买彩票的某个人来说,不可能有什么重要意义,但是对于食品杂货店经理来说, 发现有些买了快餐的顾客也常会买点冷冻食品,那就是一条很有意义的信息了。同样,数据挖 掘也包括了大量的道德方面问题,包括在数据仓库中表述的个体的权利、所得结论的准确性和 用处,甚至涉及数据挖掘初衷是否恰当。 问题与练习 : - . _ ::::-: •. 1. 为什么数_掘不在“联机”数据库上实施 f 9.7 数据库技术的社会影响 299 2. 试举出另一个模式的例子,要求文中提到的每种数据挖掘类型都可以在此例中找到。 3. 给出几种不同的观点,可以在挖掘销售數据中用到多维数据集。 " 4. 数据挖掘与传统数据库查询有何不同? 9.7 数据库技术的社会影响 随着数据库技术的发展,以往不可能得到的信息现在可以获取到。许多情况下,自动化图 书馆系统记录了每个用户的阅读记录,零售商保存了每个客户的购买记录,因特网搜索引擎保 留了客户端的请求记录。而且,这些信息对以下群体也具有潜在的 价值: 市场营销公司、法律 实施机构、雇主以及私有个体。 这代表了滲透到数据库应用整个范围的潜在问题。基于现在的技术,收集大量的信息、合 并或比较不同的数据集合以获得它们之间的关系,变得非常容易,而这些信息以前则是不可获 取的。这种衍生物(如同是一把双刃剑)非常庞大,它不仅是学术界辩论的主题,更是真实存 在的事实。 现在的数据收集工作在有些情况下比较明显,而在有些情况下就显得比较微妙了。前一种 情况的例子是直接要求某人提供信息。这可能以自愿的方式进行,如民意调查或竞赛登记等形 式; 也可能以非自愿的方式进行,如以政府规定强制进行等。有时,自愿与否取决于个人的观 点。当申请借贷时提供个人信息,是自愿还是非自愿?这种不同取决于获得贷款是为了方便还 是必需的。现在有些零售店使用信用卡时,要求以数字化格式记录签名。同样,提供这种信息 是否自愿,也是取决于所处的环境。 数据收集比较微妙的情况下,就避免了与对象直接进行交流。这样的例 子有: 信用卡公司 记录下了信用卡持有者的所有购物活动,网站记录下了访问者的身份,社会活动调查员记录下 了停在目标单位停车场的汽车的车牌号。在这些情况下,数据收集的对象不会意识到他们的信 息被收集,更不大可能知道存在为此建立的数据库。 有时候,如果停下来想想,这种潜在的数据收集活动就很清楚了。例如,杂货店可能会为己 经登记过的常客提供折扣。登记过程中可能要发一个身份认证卡,在购物时要出示该卡才能享受 折扣。这样商店就可以收集大量客户的购物记录,而这种记录的价值远远超出了折扣的价值。 当然,推动数据收集繁荣发展的动力就是数据的价值,它的作用因数据库技术的发展而得 到扩大,这些数据库技术使数据能够联系起来,这就揭示出了原本隐藏的信息。例如,对信用 卡持有者的消费模式进行分类和交叉列表,就能获得极具市场价值的顾客资料概况。利用这些 信息,健美杂志就可向那些最近买过健身器材的人寄去订阅单,而驯狗杂志的订阅单则会寄给 那些前不久买过狗食的人。有时候,信息的组合方式实在是富有^|象力。如将犯罪记录与社会 福利记录进行对比,可以找到和抓获假释期间的违 法者; 1984年美国的义务兵役机构利用从一 家著名的冰淇淋店获得的生日登记表,找出了那些逃避兵役登记的公民。 有一些方法能够用来保护社会,防止数据库滥用。 一 种办法就是通过法律手段。但是,通 过一个法案来反对一种行为,仅仅是让这种行为不合法,但阻止不了行为的发生。最好的例子 是1974年美国通过的隐私权法案,其目的是保护公民,防止政府滥用数据库。该法案中一个条 款规定政府部门要在联邦注册署公布其数据库通告,允许公民访问和纠正他们的个人信息。然 而,政府部门却迟迟不能遵照这个条款。这倒并非一定说明那些部门是出于什么恶意的目的, 在许多情况下,是属于官僚作风的问题。但是,官僚机构构建的人事数据库不能^ ■效鉴 别身份 这样一个事实,却是令人不安的。 300 第 9 章数据库系统 另一个也许更有效地控制滥用数据库的办法是公众舆论。如果损失大于好处,人们就不会 去滥用数据库,而且,企业最害怕的惩罚就是负面的公众舆论,因为这将直击要害。20世纪90 年代初期,正是公众舆论阻止了一些主要信贷机构为商业用途出售其邮件列表。更近一点的例 子,美国在线(一家主要的因特网服务提供商)在公众压力下,放弃了向电话销售员出售客户 相关信息的政策。即使是政府机构也会向公众舆论妥协。1997年,美国社会保障局修改了通过 因特网查阅社会保障记录的计划,这是因为公众舆论对信息的安全性产生了质疑。在迫于公众 舆论压力的情况下,几天就能得到结果,这与冗长的司法过程完全不同。 当然,在许多情况下,数据的持有者和数据的主体都受益于数据库应用,但是在所有情 况下,都不能轻视隐私的丢失。当信息准确时,私密性问题就比较 严重; 而当信息错误时, 秘密性问题就变得硕大无比。当意识到自己的信用度受到了错误信息的负面影响时,你可以 想象出那种无望的感觉。不难想象,在一个错误信息很容易被传开的环境里,问题会怎样地 扩大。 一般来说,秘密性问题是并且仍将是技术(特别是数据库技术)进步带来的一个主要副作 用。要解决这些问题就需要我们成为有素质的、警觉的、积极的公民。 ..,… , ......' I , : ,- ,: : I : : . I • ■, , . ! :: J !! : ;: : ; ::; 1 ...• •: V :-: ; :-: ...”::: •: " ■ I 1 : : Ii : : :1 .丨 '; :' . .|1 I • ; II :1 . • ,i :: - •:•,: :v ; ! -: : i ! i . n . ::' :, .: :, : '::■■: :: : ... - " :::- '•- ■: ■: ::: . , ' : . .1 . . : . :: : .. , „ . : : :: .. ' : • . ..... ; ; , , . ::; :: ::'•- '; . . : ; .1 1 ; •:::':;:: : .: :. . i : : 1•瘥瞢‘法部 W 为了确 萣有__向観4補钫询数据库的利,即 使这些 人并无前科? 2- 是否能紙予挺险公司为了确认有潜&健康尙题的人而访问数据库的权利,._即便这些人并无任何症 状? 3. 假綠你的縴济情况良好。如果这个 信息* 很多机构传开,+ 从中你会获得什么好处?同样信息的散布, 又会有 ff 么不刹?又若你的经济情况不理想:,结果又将如何? - 4. 新闻退版售由在控制数据库的滥用土 起到# 么作用?(例如,新闻影晌公务幾论或曝光数据库滥用 到了何稀鑛? ) - -; 复习题 (带*的题目涉及选读小节的内容。) 1. 概述平面文件与数据库之间的不同。 2. 数据独立性是什么意思? 3. 在数据库实现的层次化方法中, DBMS 的作用 是什么? 4. 模式和子模式有什么不同? 5. 指出把应用软件与 DBMS 分离的两个好处。 6. 描述抽象数据类型(第8章中讲到的)与数据 库模型的相似之处。 7. 说出下列情况或活动在麵库系统(用户、应用 软件程序员、 DBMS 软件设计者)中发生的 级别: a . 数据在磁盘上怎样存储效率才最高? b . 243航班还有空位吗? c . 在大容量存储器中,关系应当如何组织? d . 允许用户敲错几次口令才终止对话? e . 怎样才能实现 PROJECT 运算? 8. 下列哪一项工作是由 DBMS 完成的? a . 确保用户对数据库的访问权限制在相应的 子模式内。 b . 把基于数据库模型的一些指令翻译成对实 际数据存储系统的活动。 c . 隐藏数据库中的数据分散在网络中的许多 计算机内这一事实。 9. 在一个关系数据库中,怎样表示以下有关航空 公司、航班(对某一天而言)和乘客的 信息: 航空公司 : Clear Sky、Long Hop、Tree Top Clear Sky 的 航班: CS 205、 CS 37、 CS 102 Long Hop 的 航班: LH 67、 LH 89 Tree Top 的 航班: TT 331、 TT 809 Smith 已预订 CS 205 (12 B 座)、 CS 37 (18 C 座) 和 LH 89 (14 A 座)。 Baker 已预订 CS 37 (18 B 座)和 LH 89 (14 B 座)。 Clark 己预订 LH 67 (5 A 座)和 TT 331 (4 B 座)。 复习题 301 10. 对一个关系应用 SELECT 和 PROJECT 运算的次 序有什么意义?或者说,在怎样的条件下,先 做 SELECT 再做 PROJECT 的结果,与先做 PROJECT 运算再做 SELECT 操作的结果一样? 11. 给出一个论据,证明(如 9.2 节描述的)在 JOIN 运算中 where 子句是不必要的。(也就是说, 要证明任何用到 whe re 子句的查询语句都能 够通过下面这样的方式重新 表示: 用 JOIN 操 指令序列来回答以下几个有关图 9-5 中的 EMPLOYEE 、 JOB 和 ASSIGNMENT 关系中信息的 问题: a . 获取公司员工姓名和地址清单。 b . 获取在人事部工作和曾经工作过的人员姓 名和地址清单。 c . 获取正在人事部工作的人员姓名和地址 清单。 作把一个关系中的每一个元组与另一个关系 中的每一个元组连接起来。) 12. 对下列关系,执行以下各指令后,关系 RESULT 是怎 样的: X 关系 U V W A Z 5 B D 3 C Q 5 Y 关系 R S 3 J 4 K a . RESULT—PROJECT W from X b . RESULT—SELECT from X where W = 5 c . RESULT—PROJECT S from Y d . RESULT—JOIN X and Y where X.W ^ Y.R 13. 根据以下数据库,利用 SELECT、PROJECT 和 JOIN 命令,写出指令序列来回答下列有关 部件与生产商的 问题: PART 关系 PartName Weight Bolt 2X 1 Bolt 2Z 1.5 Nut V5 0.5 MANUFACTURER 关系 CompanyName PartName Cost Company X Bolt 2Z .03 Company X Nut V5 .01 Company Y Bolt 2X ■02 Company Y Nut V5 .01 Company Y Bolt 2Z .04 Company Z Nut V5 ■01 a . 哪些公司生产了 Bolt 2 Z ? 16. 用 SQL 回答上题。 17. 设计一个包含作曲家、生平及其作品信息的关 系数据库(避免类似于图 9-4 中的冗余)。 18. 设计一个包含乐队、唱片以及所录乐曲的作曲 者信息的关系数据库(避免类似于图 9-4 中的 冗余)。 19. 设计一个包含计算设备生产商及其产品的关 系数据库(避免类似于图 9-4 中的冗余)。 20. 设计一个包含有关出版商、杂志及订户信息的 关系数据库(避免类似于图 9-4 中的冗余)。 21. 设计一个包含有关零部件、供应商及客户信息 的关系数据库。每种零部件可有几个供应商供 应,并可有多个客户订购。每个供应商可以供 应许多种零部件,也可有多个客户。每个客户 可以向多个供应商订购多种零部件;实际上, 同一种零部件可向一个以上的供应商订购(避 免类似于图 9-4 中的冗余)。 22. 写出指令序列(利用 SELECT 、 PROJECT 及 JOIN 运算)来实现从图 9-5 所示的关系数据库 中检索财务部每个职务的 Jobld、StartDate 及 TenuDate 。 23. 用 SQL 回答上题。 24. 写出指令序列(利用 SELECT 、 : PROJECT 及 JOIN 运算)来实现从图 9-5 所示的关系数据库 中检索现任每位员工的 Name 、 Address 、 JobTitle 及 Dept 0 25. 用 SQL 回答上题。 26. 写出指令序列(利用 SELECT 、 PROJECT 及 JOIN 运算)来实现从图 9-5 所示的关系数据库 中检索现任每个员工的 Name 及 JobTit le 。 27. 用 SQL 回答上题。 28. 由单一关系 b . 获取一个由 Company X 生产的部件及其价 格清单。 c . 哪些公司生产重量为1的部件? 14. 用 SQL 回答第13题。 15. 利用 SELECT 、 PROJECT 和 JOIN 命令,写出 Name Department TelephoneNumber Jones Sales 555-2222 Smith Sales 555-3333 Baker Personnel 555-4444 和两个关系 302 第 9 章数据库系统 Name Department Jones Sales Smith Sales Baker Personnel Department TelephoneNumber Sales 555-2222 Sales 555-3333 Personnel 555-4444 提供的信息有什么不同? 29. 设计一个包含汽车部件及其子部件的关系数 据库。要做到 :一个 部件可以包含更小的零件, 同时它本身可以是更大部件的零件。 30. 选择一个常用的网站,像 www . google . com 、 www . amazon . com 或 www . ebay . com , 设计一个 关系数据库,作为网站的支持数据库。 31. 基于图 9-5 所示的数据库,说明以下程序段回 答的 问题: TEMP—SELECT from ASSIGNMENT where TermDate - " RESULT—PROJECT Jobld, StartDate from TEMP 32. 把上题中的查询翻译成 SQL 语句。 33. 基于图 9-5 所示的数据库,说明以下程序段回 答的 问题: TEMPI—JOIN EMPLOYEE and ASSIGNMENT where EMPLOYEE. Emplld - ASSIGNMENT. Emplld TEMP2—SELECT from TEMPI where TerinDate = " * " RESULT—PROJECT Name, StartDate from TEMP2 34. 把上题中的查询翻译成 SQL 语句。 35. 基于图 9-5 所示的数据库,说明以下程序段回 答的 问题: TEMPI—JOIN EMPLOYEE and JOB where EMPLOYEE. Emplld 二 JOB■ Emplld TEMP2—SELECT from TEMPI where Dept="SALES n RESULT ^ PROJECT Name from TEMP 2 36. 把上题中的查询翻译成 SQL 语句。 37. 把 SQL 语句 select JOB.JobTitle from ASSIGNMENT,JOB where ASSIGNMENT.Joblld 二 JOB.Jobld and ASSIGNMENT - Emplld = "34Y70" 翻译成 SELECT 、 PROJECT 及 JOIN 运算的 序列。 38. 把 SQL 语句 select ASSIGNMENT-StartDate from ASSIGNMENT, EMPLOYEE where ASSIGNMENT ■ Emplld = EMPLOYEE.Emplld and EMPLOYEE.Name = "Joe E. Baker M 翻译成 SELECT 、 PROJECT 及 JOIN 运算的 序列。 39. 说明对第 13 题中数据库执行以下 SQL 语句后 的 效果: insert into MANUFACTURER values( 1 Company Z 1 , 'Bolt 2X', .03) 40. 说明对第 13 题中数据库实施以下 SQL 语句后 的 效果: update MANUFACTURER set Cost=.03 where CorapanyName 二 'Company Y 1 and PartName = 1 Bolt 2X■ *41. 请确定用来维护杂货店库存的面向对象数据 库中的几个对象,并说明每个对象中应该包含 哪些 方法? *42. 请确定用来维护图书馆藏书记录的面向对象 数据库中的几个对象,并说明每个对象中应该 包含哪些方法? *43 .如果 T 1 和 T 2 两个事务按如下安排来调度,会 产生 什么样的错误信息? T 1 设计为计算账户 A 和 B 总和, T 2 设计为从账 户 A 转账100美元到账户 B 。 T 1 先读取账户 A 的 余额,然后 T 2 执行转账,最后 T 1 读取账户 B 的 余额,并输出读得的两个值的总和 。 *44. 说明怎样用文中介绍的锁定协议来解决问 题43 。 *45 .第43题中如果 T 1 是较新的事务,那么受伤等 待协议会对上述事件序列起什么作用?如果 T 2 是较新的事务,结果又如何? 社会问题 303 *46. 假设有一个事务试图在一个余额为200美元的 账户中存入100美元,同时另一事务试图从同 一账户取出 10.0 美元。描述如何通过这些事务 的交叉处理,使得最后余额为100美元。描述 如何通过这些事务的交叉处理,使得最后余额 为300美元。 *47. —个事务对数据库中的一个数据项,有互斥访 问和有共享访问有什么不同?为什么这种差 别很重要? M 8. 9.4 节讨论过的关于并发事务的一些问题不仅 限于数据库环境。当用文字处理程序来访问同 一个文挡时会产生怎样类似的问题?(如果你 的 PC 机中有字处理程序,试着激活两个实例 来访问同一文档,看看会发生什么。) *49. 假定一个顺序文件有50 000条记录,查询一条 记录需5 ms 。 请问: 检索一条处在文件中间部 位的记录,需要等待多长时间? *50. 见图 9-15 中的归并算法,如果其中一个输入文 件一开始就是空的,请列出执行该算法的步 骤。 *51 .修改图 9-15 中的算法,来处理这样一种 情况: 两个输入文件都包含一个有同样键字段值的 记录。假定这些记录都一样,在输出文件里只 要有一个即可。 *52. 设计一个系统,利用这个系统,存储在磁盘上 _的一个文件能够作为顺序文件以两种不同的 顺序能进行处理。 *53 .说明如何能够利用一个文本文件作为基本结 构,来构建一个包含杂志订阅者信息的顺序文 件。 *54. 设计一种技术,通过这种技术,将逻辑记录大 小并不一致的顺序文件作为文本文件来实现。 例如,假设要构建一个包含小说家信息的顺序 文件,其每条逻辑记录都包含一个作家的信息 及其作品清单。 *55. 索引文件与散列文件相比,有什么优势?而散 列文件与索引文件相比,又有什么优势? *56. 本章描述了传统文件索引与由操作系统维护 的文件目录系统相似之处。在哪些方面操作系 统的文件目录与传统索引不同? *57. 如果散列文件分到10个桶里,那么任意3条记 录中的至少2条放进同一个桶的概率是多少 (假定散列函数不让任何一个桶拥有优先 权)?文件中必须存放有多少条记录,才可能 发生碰撞? *58 .假设文件被分进100个桶而不是10个桶,重解 上题。 *59 .如果我们利用本章讨论过的除法技术作为散 列函数,并且将文件存储区分成23个桶,那么 当把键翻译为一个二进制值时,应当搜寻哪个 区来寻找其键值为整数124的记录? *60. 通过比较散列文件的实现与同构二维数组的 实现,说明散列函数与地址多项式的作用有什 么类似之处? *61. 试给出下列比较中的一个 优点: a . 顺序文件优于索引文件。 b . 顺序文件优于散列文件。 c . 索引文件优于顺序文件。 d . 索引文件优于散列文件。 e . 散列文件优于顺序文件。 f . 散列文件优于索引文件。 *62. 从哪些方面可以看出顺序文件类似于链表? 社会问题 下面的问题有助于你分析一些与计算领域相关的道德、社会和法律问题。回答这些问题不 是唯一的目的,还应该考虑为什么这样回答,以及你的判断是否对每个问题都标准如一。 1. 在美国,所有囚犯的 DNA 记录都存储在一个数据库中,以备犯罪研究所用。如果发布这 些信息用于其他用途,例如用作医学研究,这样做道德吗?如果合乎道德,可用于什么 目的?如果不合乎道德,为什么?而每种情况的利与弊又是什么? 2. 大学能将其学生的信息公开到何种程度?可以公布他们的姓名和地址吗?可以在学生不 知情的情况下公布他们的成绩排名吗?你的看法是否与第1题的答案一致? 3- 构建有关个人的数据库时,采取什么样的限制比较合适?政府有权掌握公民的什么信 息?保脸公司有权掌握其客户的什么信息?公司有权掌握其雇员的什么信息?在这些 情况中,需要实行控制吗?如果需要,怎样实现? 304 第 9 章数据库系统 4. 如果信用卡公司把它的客户的消费模式卖给商业公司,这样做是否合适?如果赛车邮购 业务公司把它的邮购清单卖给赛车杂志,这样做是否合适?如果美国国税局把那些有着 巨额收入的纳税人的姓名和地址信息卖给股票经纪人,这样做是否合适?如果你没有充 分的把握回答是与否,那么你有什么可行的方案? 5. 数据库的设计者对于如何使用数据库信息应当负怎样的责任? 6 . 假设数据库的信息因数据库错误而被非授权用户访问。如果信息被怀有恶意目的的用户 获得和使用,那么数据库设计者应对此承担何种责任?你的回答是否与作恶者为发现数 据库设计漏洞并非法获取信息所花费的精力大小有关? 7. 数据挖掘的盛行带来了大量的道德和隐私问题。如果数据挖掘揭示了你所在社区的所有 居民的某些特性,那么你的隐私是否受到侵犯?数据挖掘的使用是促进了商业的发展还 是鼓励了盲从?因为相对于个别问卷调查明确询问的方式而言,从人口普查的数据中能 提取更多的信息,那么强制公民参加人口普查是否合适呢?数据挖掘给予商业公司的好 处,对于不知情的客户来说是否不公平?这样一种状况的好与坏,到了何种程度? 8. 可以允许公司或个人收集和保留私人信息能够到多大的程度?尽管收集的信息分散在 一些发起者之间,但是如果这些信息已经能公幵地获得,那么现在该怎么办?公司或个 人期望在何种程度上保护这类信息? 9. 许多图书馆提供参考查询服务,所以读者在查阅信息时可以得到图书管理员的帮助。因特 网和数据库技术的出现是否会使这种服务过时?如果会,那么这是前进了还是倒退了? 如果不会,为什么?因特网和数据库技术的存在对图书管理员本身有什么样的影响? 10. 你的身份信息被盗用的可能性到了什么程度?你会釆取哪些步骤使盗用机会最小?如 果你的个人信息被窃,对你的伤害将有多大?如果发生这种情况,你自己有责任吗? 课外阅读 Beg, C. E. and T. Connolly. Database Systems: A Practical Approach to Design, Implementation and Management, 4th ed. Boston, MA: Addison-Wesley, 2005. Berstein, A , M. Kifer and P. M. Lewis. Database Systems, 2nd ed. Boston, MA: Addison-Wesley, 2006. Date, C. J. An Introduction to Database Systems, 8th ed. Boston, MA: Addison-Wesley, 2004. Date, C. J. Databases, Types and the Relational Models 3rd ed. Boston, MA: Addison-Wesley, 2007. Elmasri, R. and S. Navathe. Fundamentals of Database Systems^ 6th ed. Boston,MA : Addison-Wesley, 2011 . Patrick, J. J. SQL Fundamentals^ 3rd ed. Upper Saddle River, NJ: Prentice-Hall, 2009. Silberschatz, A. H. Korth, and S. Sudarshan. Database Systems Concepts f 8th ed. New York: McGraw-Hill, 2009. Ullman, J. D. and J. D. Widom. A First Course in Database Systems, 3rd ed. Upper Saddle River, NJ: Prentice-Hall, 2008. 计算机图形学 第 in_ 量〜从.華 w ..- v : .--I ,.y .A . f 章将探索计算机图形学领域,这是一个对电影制作和交互式视频游戏具有重大影响的 领域。实际上,计算机图形学的发展解除了视觉媒体对实体的限制,许多人认为计算 机动画在不久的将来会取代整个影视产业对传统的演员、布景和照片的需求。 ..d 雌 ^ : 10.1 计算 机图形 学的范爾 10.2 3 t ) 图形 概述、 103 建模 ,, 10.4 渲染 *10,5 处赛全局照明' 计算机图形学是计算机科学的分支,它应用计算机技术创建和操控视觉表现。这是一个广 泛的主题,它 包括: 文本表示、图形和图表的创建、图形化用户界面的开发、照片的操作、视 频游戏的制作、影视动画的生成等。然而,术语计算机图形学越来越多地被用来指代 3 D 图形学 的特定领域,本章大部分内容将集中在这个主题上。我们将从定义 3 D 图形学开始,阐明它在广 义的计算机图形学中的作用。 10.6 动画 复习题 社会 H 题 课外阅读= 10.1 计算机图形学的范围 随着数码相机的出现,数字图像处理软件迅速流行起来。人们可以使用这类软件通过去除 污点和“红眼”等操作达到“润色”照片的目的,也可以在不同的照片中进行裁剪和粘贴,创 建一幅并非反映真实世界的图像。 类似的技术经常应用于电影和电视产业中,以产生特效。例如,可以很容易地通过去除支 撑的金属丝、重叠多幅图像,或产生新的图像序列帧等特效处理,改变最初拍摄的情节。这促 使影视产业摒弃像胶卷之类的模拟系统,转向数字编码的图像。 除了处理数字照片和视频的软件外,现在还有各种各样的工具/应用软件包,它们帮助产生 二维图像,从简单的画线到复杂的艺术品。(一个众所周知的最基本的例子就是微软的“画图” 应用程序。)这类程序的基本操作 包括: 绘制点和线、插入椭圆和矩形这类简单的几何图形、给 区域填充颜色,以及裁剪和粘贴绘图的指定部分。 注意,上面所有的应用都是处理平面二维图形和图像的操作。这里有两个相关研究领域, —个是 2 D 图形学 (2 D graphics ), 另一个是图 像处理 (image processing ) 0 二者的区别在于: 2 D 图形学侧重于把二维图形(圆、矩形、文字等)转化为像素模式,产生图像;而图像处理侧重 于分析图像中的像素,进行模式识别,以达到增强或“理解”图像的目的。简言之, 2 D 图形学 306 第 10 章计算机图形学 处理生成图像,而图像处理分析图像。 _ 与 2D 图形学中把二维图形转化为图像相对应, 3D 图形学 (3D graphics) 领域处理的是把三 维图形转化成图像。这个过 程是: 建造三维场景的数字化版本,然后模拟照相的过程,产生这 些场景的图像。这与传统的摄影类似,不同之处在于场景是使用 3 D 图形技术“拍摄”出来的, 实际是不存在的,存在的是数据和算法的集合。因此, 3D 图形“拍摄”虚拟世界(如图 10-1 所 示),而传统的摄影技术拍摄真实世界。 需要注意的是,使用 3 D 图形创建图像要经历两个不同的 步骤: 一个是创建、编码、存储以 及操作被拍摄出来的场景;另一个是生成图像的过程。前者是创造性的、艺术的过程,而后者 则是以计算为主的过程。这些主题是我们在下面 4 个小节中将要讨论的。 -图 10-1 使用 3 D 图形产生的虚拟世界的“照片”(迪士尼与皮克斯合拍的 《玩具总动员》剧照) © Disney/Pixar 3 D 图形可以制作出不依赖于实体的虚拟场景,这使得它非常适用于交互式视频游戏和 动画电影的制作。交互式视频游戏由数字化的三维虚拟环境构成,游戏玩家与之进行交互, 玩家看到的图像是通过 3 D 图形技术制作出来的。动画电影是用类似的方法创建的,不同之 处在于只是动画制作者与虚拟环境交互,而公众看到的则是导演/制片人发布的二维图像 帧序列。 本书将在 10.6 节中更全面地讨论 3D 图形学在动画中的应用。这里可以想象一下,随着 3D 图 形技术的发展,这些应用将可能导向何处。如今,电影是作为二维图像序列发布的。尽管显示 这些信息的放映机已经取得了很大进步,从使用胶卷的模拟设备到使用 DVD 播放机和平板显示 器的数字技术,但它们的显示仍然只是二维的。 但是,想象一下当创建和操作真实的三维虛拟世界的能力得到改善时,将会发生什么改变。 我们将不再仅能“拍摄”这些虚拟世界和以二维图像的形式发布电影,而且能发布虚拟世界。 观众将不仅仅只能观看电影,还可以通过 “3 D 图形放映机”来观看虚拟场景,就像通过专用的 “游戏盒”来观看视频游戏一样。观众可能先看到导演/制片人预定的“推荐情节”,与此同时还 可以与虚拟场景交互,就像玩视频游戏一样产生另外的场景。考虑到正在研发的三维人机接口 的巨大潜力,未来的前景十分诱人。 俩_与:- - 1- 总结图像处理、 2 D 图形学和瓣_形学之间的区别。 2, 3 D 图形学与传统瘍 論有何 不同? 3, 应用 3 D 图形学制作“照片”的两个主要步骤是什么? 10.2 3 D 图形概述 307 : •. V :-- v ~ r : ■•广 -... 10.2 3 D 图形概述 本章我们从创建和显示图像的整个过程来开始对 3 D 图形学的研究。这个过程由3步 构成: 建模、渲染 ( rendering ) 和显示。建模(将在 10.3 节中详细介绍)与传统电影产业中设计和构造 一个场景类似,不同之处在于 3 D 图形场景是用数据结构和算法“构造”的。这就导致应用计算 机图形学产生的场景可能在现实中永远都不存在。 下一步就是通过计算场景中的物体如何显示在由特定位置的相机拍摄的照片中,来生成场 景的二维图像。这称为渲染 ( rendering ,10.4 节和 10.5 节的主题),渲染的概念是运用解析几何, 来计算场景中的物体到一个称为投影平面 ( projectionplane ) 的面上会形成的投影,这种方式与 相机将场景投影到胶卷上的方式类似(如图 10-2 所示)。这种投影称为透视投影 (perspective projection ), 在这种投影方式下,所有的目标都沿着一条称为投影线 ( projector ) 的直线向前延 伸,这条直线是从一个称为投影中心 (center of projection ) 或视点 (view point ) 的公共点延伸 出来的。这与平行 1 投影 (parallel projection ) 不同,顾名思义,平行投影线是平行的。透视投影 产生的投影类似于人类眼睛所看到的,而平行投影产生的是物体“真正”的剖面,这在工程绘 图中非常有用。 图〗 0-2 3 D 图形学范例 对于用来定义最终图像边界的投影平面,其中受限的部分称为图像窗口 (image window )。 它对应于显示在大多数相机取景器上的矩形,指明潜在图像的边界。实际上,大多数相机的取 景器允许用户看到相机投影平面上更大的区域,而不仅仅是图像窗口。(你可能会在取景器中看 到“玛莎阿姨”头部的上方,但是,除非这部分影像也出现在图像窗口中,否则它就不会出现 在最终图像中。) 投影到图像窗口的场景部分确定了以后,就可以计算出最终图像上的每个像素点的显示情 况,这种逐个像素的计算过程可能会很复杂,因为它需要确定场景中的物体如何与光线融合。 (在明亮光线下硬且有光泽的表面与在间接光线下软且透明的表面,二者的渲染方法应该有所不 同。)因此,渲染处理涉及包括材料科学和物理学在内的许多其他研究领域。而且,在决定一个 物体的显示效果时经常需要了解场景中的其他物体。这个物体可能处在另一个物体的阴影中, 或者这个物体是镜子,它的外观实质上就是其他物体。 当确定了每个像素的外观后,结果被集中地表示成图像的位图,并存储在称为帧缓冲区 (frame buffer ) 的存储区域中。这个缓冲区可能是主存中的一个区域,或当有专门处理图形应用 的硬件时,它可能是专用存储电路中的一个块。 最后,存储在帧缓冲区的图像或者为了观看而显示,或者为以后的显示而传送给更永久的 308 第 10 章计算机图形学 存储器。如果生成的图像将用于电影画面,那它可能在最终显示前被存储甚至是被修改。但是, 在交互式视频游戏或飞行模拟器中,图像必须显示,因为它们是在实时生成的,这个要求经常 限制了图像的质量。这就是由制片厂发布的功能齐全的动画产品的图像质量要超过当今交互式 视频游戏中的图像质量的原因。 最后,我们通过分析一个典型的视频游戏系统来结束对 3 D 图形的介绍。这个游戏实际上就 是一个编码的虚拟世界和软件的结合体,它允许游戏玩家操控这个虚拟世界。当玩家操控这个 世界时,游戏系统会不断地渲染场景并把图像存储到图像缓冲区中。为了克服真实世界的时间 限制,大多数渲染处理都是由专用硬件来实现的。实际上,正是这些硬件使游戏系统和一般个 人计算机之间有了显著差别.最后,游戏系统中的显示设备显示了帧缓冲区中的内容,给玩家 以变化场景的幻觉。 间题為练习 : . 1 .. ;;!•;';. ::: : : : UI: 龜鳝在雙無齒賴瞻 ■ 2. 投影平德和图像窗口之间有何本伺? 3. 什么是帧缓冲区? 10.3 建模 3 D 计算机图形投影的起始阶段与戏剧舞台制作方式十分 相似: 必须设计出布景,收集或者 搭建所需的道具。在计算机图形学的术语中,布景称为场景 ( scene ), 道具称为物体 ( object )。 记住, 3 D 图形场景是虚拟的,因为组成它的物体是由数字化编码模型“构建”而成,并不是实 际的物理结构。 本节将探讨与“构建”物体和场景有关的话题。我们以单个物体的建模问题开始,并以考 虑收集这些物体以形成场景这个任务结束。 10.3.1 单个物体的建模 在舞台制作中,道具的真实程度取决于它在场景中的使用方式。我们可能不需要一辆完整 的汽车,电话并不需要能用,背景可能也只是画在大背景屏幕上的。同样,就计算机图形学而 言,一个物体的软件模型能准确地反映物体真实属性的程度依赖于情境的需要。前景物体的建 模与背景中的物体相比需要考虑更多的细节。而且,在那些没有严格实时限制的情况下,会产 生更多的细节。 因此, 一 些物体模型可能相对简单,而另一些可能极其复杂。作为一个通用规则,模型越 精确,图像的质量越高,渲染所需要的时间也就越长。因此,现在进行的大多数对于计算机图 形学的研究都是在寻求一种开发技术,以构建非常精细,同时又不失高效的物体模型。这些研 究中有些涉及开发模型,开发模型依据物体在场景中的最终作用来提供不同的细节层次,这样 可以在变化的场景中重用同一个物体模型。 描述一个物体所需的信息 包括: 物体的形状,以及额外的特性(如决定物体如何与光线交 互的表面特性等)。现在,让我们考虑形状建模这个任务。 1. 形状 在 3 D 图形中物体的形状通常描述成称为 平面片 ( planarpatch ) 的小平面的集合,其中每一 个都是一个多边形。这些多边形形成了多边 形网格 (polygonal mesh ), 它近似于被描述的物体 10.3 建模 309 的形状(如图 10-3 所示)。通过使用小平面片,近似值可达到所需的精度。 多边形网格中的平面片经常选择三角形,因为每个三角形能用它的3个顶点来表示,这是在 三维空间中确定一个平面所需点的最少数目。在任何情况下,多边形网格都表示成送些平面片 顶点的集合。 一个物体的多边形网格表示可以通过多种途径获得。其中一 种是: 以所需形状的精确的几 何描述开始,然后用这些描述构建多边形网格。例如,解析几何中半径为 r 的球(中心在原点) 用方程来描述: 基于这个公式,我们可以建立球上经线和讳线的方程,标识这些线的交叉点,然后使用这 些点作为多边形网格中的顶点。类似的技术可以应用到其他传统的几何形状上,这就是为何在 廉价的计算机动画中人物角色经常甲球、圆柱体和锥体这些结构拼凑的原因。 更-般的形状可以用更复杂的分析方法来描述。其中一种方法是使用贝塞尔曲线 (Bezier curve )( 以皮尔•贝塞尔命名,他在19世纪70年代早期提出了这个概念,当时他是雷诺汽车公 司的工程师),它允许在三维空间中只用几个称为控制点的点来定义曲线段(其中有两个点表示 曲线段的端点,而其他的点则指出曲线的弯曲方式)。例如,图 10-4 显示了由4个控制点定义的曲 线。注意,曲线显示为弯向两个不为端点的控制点。通过移动这些点,曲线可以被扭曲成不同的 形状。(你可能曾经用过像微软的画图软件这样的绘图软件包构建曲线。)尽管我们在这里不再继 续探讨这个话题,但描述曲线的贝塞尔技术可以扩展为描述三维曲面,称为贝塞尔曲面 (Bezier surface )。 因此,对于复杂表面,在获得多边形网格的过程中,贝塞尔曲面被证明是高效的第一步。 图 10-3 球的多边形网格 标识曲线端点的控制点 _ 一 一一用来扭曲曲线的控制点 图 10-4 贝塞尔曲线 你可能会问为什么需要把形状的精确描述(如球的简明公式,或描述贝塞尔表面的公式) 转化为使用多边形网格的近似描述。答案是用多边形网格表示所有物体的形状确立了渲染处理 的统一方法——可以更髙效地渲染整个场景的技巧。这样,尽管几何公式提供了形状的精确描 述,但它们只是作为构建多边形网格的工具。 另外一种获得多边形网格的方法是用蛮力的方式构建网格。在无法用优雅的数学技术表示 形状的情况下,这种方法就比较常见了。在这个过程中,首先构建物体的物理模型,然后用类笔 设备触摸表面,记录下模型表面点的位置,这种笔设备能记录它在三维空间中的位置,此过程就 称为数字化 ( digitizing )。 然后将获得的点的集合用作顶点,从而获得所描述形状的多边形网格。 遗憾的是,有些形状非常复杂,难以用几何建模或手工数字化获得真实模型。这些例子 包括: 复杂植物结构(比如树)、复杂地形(比如山脉),以及云、烟、火苗等气态物质等。在这些情况 下,多边形网格可以通过编写自动构建所需形状的程序来获得。这样的程序统称为程序化模型 10.3 建模 311 在上文中描遂了 if 程序化模型构建 :山脉 (参见图 io 3 ),逢是 命窄推 濟于敢图:彩:的例子。 从技术上讲,分形 ( fractal ) 是 “ Hausdorff 维度大乎其拓 扑蝽度 ,,的物体。直观上讲, 这意味着特体是琢过低维度物体的副本“打包”而弗成的(想象一度就是通过“打包” 多条平衧线段而创_的。)分形:通常是傀用递归过程来形成的,=而在递系¥妁每个处理就是重 复“打包”另外用来建立分形模式 ( 更小)的副本。分形的结果是甚每乎部分都是自相似的, 当放大时,它显示为自身的副本* , I ; . :: : 1 ' 分形的一个传统的示例妩 A 科4雪乾,它臭通过重复4滅相 M 鋒构铋枝 Wk 本♦换结趣 ,: ; :: [• • : : : " .: •. •::.'"• . ::' ':•;':•' ■"::■ : •: :. ... v : : ;0 v ;:: ' ' ' | ! • i : : !' .• : r: . 中的直线段而形 成:喻 ife_ ■ 生成的细化序列如下所示: .": I :: .j,•::":' :i s - 沒择 i__i 分形在 3 D 图形领域经常是租序 化模型 的主干,声际上,贫狗已绿被肩来生成填事的山脉、 蔬菜、云帝烟的图:豫:。 程序化模型的输出通常是近似于物体形状的多边形网格。在某些情况下,如使用三角形生 成山脉,网格就是生成过程的自然结果。在另外一些情况下,如应用分支规则生成树,网格可 能就是额外的、最终的步骤。例如,在粒子系统中,系统外边沿上的粒子自然会被选作最终多 边形网格中的顶点。 由程序化模型生成的网格的精度视具体情况而定。在场景中,用于背景中的树的程序化模 型可能只要产生一个反映树基本形状的粗糙的网格,而前景中的树的程序化模型就要产生能分 清各个枝叶的网格。 2 . 表面特征 仅由多边形网格构成的模型只捕获了物体的形状。大多数渲染系统能在渲染过程中丰富这 些模型,根据用户的需求模拟各种表面特征。例如,通过使用不同的着色技术(我们将在 10.4 节介绍),用户可以指定球的多边形网格被渲染成光滑的红球或是粗糙的绿球。在某些情况下, 这种灵活性是可以做到的。但在需要如实渲染原始物体的情况下,关于物体的更具体的信息必 须包含在模型中,这样渲染系统才会知道该干什么。 除了形状 之外, 还有多种有关物体信息的数字化技术。例如,沿着多边形网格的每个顶点, 人们可以在物体的这一点上指定原始物体的颜色。然后在渲染过程中用这些信息重新创建原始 物体的外观。 在其他的例子中,通过称为纹理映射 (texture mapping ) 的处理,颜色模式能与物体表面相 关联。纹理映射类似于贴墙纸,将一个预定义的图像与物体的表面相关联。这个图像可能是数 字照片、艺术家的绘画,也可能是计算机生成的图像。传统的纹理图像包括砖墙、有木纹的表 面和大理石表面等。 例如,假设我们需要对石墙建模,我们可以用描述长矩形体的简单多边形网格来表示墙的 形状。利用这些网格,我们就能提供砖石结构的二维图像。随后,在渲染过程中,将这个图像 312 第 10 章计算机图形学 映射到矩形体上,产生石墙的外观。更准确地,每当渲染处理需要显示墙上的点时,它就只需 显示砖石结构图像中对应的点。 当应用于相对平坦的表面时,纹理映射的效果最好。如果必须大幅扭曲纹理图像去覆盖弯 曲的表面(想象成试图给一个沙滩球贴墙纸的问题),或者如果纹理图像完全裹着一个物体,并 导致了接缝,在接缝处纹理模式可能不与它本身融合,那么效果看上去会不够逼真。不过,纹 理映射己经被证明是一种模拟纹理的有效方法,它被广泛地用在需要实时显示的场合(一个基 本的例子就是交互式视频游戏)。 3. 寻求逼真的效果 构建可以产生逼真图像的物体模型是一个正在研究的主题,特别有趣的是当前角色的材质, 如皮肤、头发、毛皮和羽毛等。这些研究大多是针对特殊物质的,包括建模和渲染技术。例如, 为了获得人类皮肤的逼真模型,有些学者研究光渗透到表皮和真皮层的程度以及这些层的厚度 对皮肤外观的影响。 另一个例子是人类头发的建模。如果从远距离来看头发,那么传统的建模技术就足够了。 但是,从近距离看,头发的显示将会是一个挑战。其中的问题 包括: 半透明的特性、纹理的深 度、遮盖特性和头发响应像风这样的外力的方式。为了解决这些棘手的问题,有些应用程序转 向对单根头发建模。(这是一个可怕的任务,因为人的头发根数的数量级达到了 100 000。)但是, 更令人惊奇的是有些研究者已经建立了头发模型,这个模型给出了单根头发的鳞状纹理、颜色 变化和机械动力学特征等。 另外一个己经发展到相当精确建模程度的例子是布的建模。在这个例子中,利用编织模式 的复杂细节,来生成织物类型(像斜纹布与缎布)之间恰当的纹理差别。将纱的细节特性与编 织模式数据组合在一起,创建出编织物的模型,产生逼真的特效图像。例如,注意在图 10-6 中 显示的史莱克袖子上的细节(还有被史莱克抱在手中的魔法猫所戴帽子上逼真的羽毛)。另外, 还将物理和机械工程的知识应用到计算织物材料图像的单根线上去,以说明线的拉伸和织物修 剪方面的特性。 图]0-6由梦工厂 SKG 制作的《怪物史莱克2》中的场景 (© Dreamworks/Picture Desk Inc./ Kobal Collection) 正如我们所说,生成逼真图像是一个活跃的研究领域,它综合了建模和渲染处理中的技术。 一般来说,当取得进步时,新技术会首先应用在那些不受实时限制影响的程序中,如电影制片 厂中的图形软件,建模/渲染处理与最终的图像显示之间有着明显的延迟。这些进步可以通过仔 细比较迪士尼的影片《玩具总动员2》 (1999 年)中的角色与最初的《玩具总动员》 (1995 年)中 10.3 建模 313 的人物而观察到。新近开发的技术被用来改善表示面部特征的多边形网格间的接缝,如鼻子与 脸的其他部分的边界。当这些新的技术得到进一步发展并变得更高效时,它们就可以应用在实 时系统中了,在这些环境中的图形质量也得到了改善。真正实现与虚拟世界的实时交互可能己 经不太遥远了。 10.3.2 整个场景的建模 场景中的物体己经得到充分的描述和数字化编码,它们就都被赋予了场景内的位置、大小 和方向。将这些信息集合并链接起来以形成一个数据结构,称为场景图 (scene graph )。 此外,场 景图还包含与表示光源及相机的特殊物体的链接,其中记录了相机的位置、方向和焦点等特性。 因此,生成场景图类似于在传统的工作室中摄影。它包括布置相机、灯光、道具和背景。 (当按快门时,所有的东西都对照片的外观产生了影响。)所不同的是传统照片设置包含物理实 体,而场景图包含的是物体的数字化编码表示。简而言之,场景图描述的是一个虚拟的世界。 为了强调场景图的范围,再次考虑图 10-1 中的图像,想象一下用来生成它的场景图。人物、 墙、床单、床柱、巴斯光年(太空突击队员)身后的背包、窗子的线脚、窗外的树和光源都以 各自适当的精度得到了建模,并表示在场景图中。事实上,最初你可能把物体看成单个的结构, 如 Woody (牛仔玩偶),但实际上它们在场景图中是聚集在一起的。 场景中相机的位置会对图像产生很大的影响。正如先前提到的,物体建模的精度依赖于物 体在场景中的位置。前景物体比背景物体需要更精确的建模,前景和背景的区别依赖于相机的 位置。如果使用的场景环境类似于戏剧舞台布景,那么前景和背景就很好区分,物体模型也能 被相应地构建。但是,如果环境要求对于不同的图像相机的位置是改变的,那么由物体模型提 供的精度就需要在“照片”间进行调整,这是当前研究的一个领域。一种方法是想象场景是由 “智能”模型构成,当相机在场景中移动时,这些模型重新修改了它们的多边形网格和其他特性。 移动相机情景的一个有趣的例子发生在虚拟现实系统中,用户可以借助它来体验在虚拟的 三维世界里走来走去的感觉。虚拟的世界用场景图表示,而人通过操控相机来观察其中的场景。 实际上,为了提供三维的深度感,可以使用两个 相机: 一个表示人的右眼,另一个表示人的左 眼。通过显示由每只眼睛前的相机获得的图像,人们产生了居住在三维场景中的幻觉。当在体 验中增加声音和触觉时,这种幻觉就变得十分逼真。 最后,我们应该注意到场景图的构建在 3 D 图形处理中非常重要。因为它包含了生成最终图 像所需的所有信息,它的完成志着艺术建模的终止和以计算为主的图形渲染的开始。实际上, 建立场景图以后,图形学的任各就变成了计算投影、确定特定点的表面精度和模拟光效。这些 任务在很大程度上与特定的应用无关。 Vi '、 ㈣“ 也恭 ‘ M ,^ j ,.^ 1 df? … 4〆 设 ㈤ 鐵政 ! : : J W:; 卜 h . 卜 !A K 卜 W 乂 1 现在有一些枝术能够在电桃中敷滅 3: D 影初:,但都很赖于同^ 种立板的视 觉效果,_对于 左右眼看 到的两 个考讀不同的图像,大脑可以依此判断出深度。就这一貧面而言,现今最使 宜的装皇需要具有滤,光镜。较老的有色眼镜 (20 世纪50年代用于电影院中)或更现代的偏振 光眼镜会过滤掉屏幕上同一图像的不同方面,从而使左右眼看到不同的图像。较昂贵的技术 则需要#主动式怏 HI3D 眼镜,它与快速切换左右图释的 31> 电视同步地交替开关左右镜片。最 后,不需要特殊眼镜或头部装置的 3 D 电视正在研发之中,它们在哥幕表面精心排放一组滤光 镜或放大镜 ,,从 而将左七图像以稍稍本同的角度投射给地着者', U 而使观看¥的左右眼着到 不同的图像。 ' 314 第 10 章计算机图形学 l 卞 :( 便传载 fe 貪 :微樂 标索編跸)、 古们 表#乎解片的_点。:肩雄爾 ft 的濟状。(対于没 ; 有择折寰茶兰元組表示: A 赓婦#:無一个角麗如钶霜遥钶^ 第一个敷 广表示沿-地于減右 辍沾璋 教與:的赛缠 嫌多鵠南二个 数表示輪与德你犮&輝墙 麵 __^__ 画___圓 _ 儀纏 _ 圓議 ______ 3:. 列出一聲 在生七 一个公涵#:德舞场:景■中,出规«物鉢 ;。 v 」:乂… , 4. 琢然可-嵐凡%泰程吏精确表示治么:坯要用-边形:科格芣轰示 | … | 議 _ 圈 ® IM__I__ 園 ^ - L r«n • T--i*r- -— ,w w-j.* - • v '--.i.-j.vj.v v 10.4 渲染 l 細 di ■话••跑 ,r t 现在让我们考虑渲染处理,它决定了当场景图中的物体投影到投影平面时,将如何显示。 有几种方法可以完成渲染任务。这一节着重介绍当今“消费者市场”上流行的图形系统(视频 游戏、家庭计算机等)所使用的传统方法。下一节将讨论其他两个可供选择的解决方案。 首先探讨光和物体间交互的一些背景信息。毕竟,物体的外观是由从物体发出的光决定的, 因此确定物体的外观这一任务最终变成了对光的特性的模拟。 10 A 1 光-表 面交互 依赖于物体的材料特性,照射到其表面的光可能被吸收,从表面反弹成反射光,或穿过表 面(被弯曲)成折射光。 1. 反射 让我们考虑从一个平坦不 it 明表面反射的光线。光线沿直线传播,以一个角度照射到表面 上,这个角度称为 入射角 (incidence angle ) 。光线的反射角与入射角相同,如图 10 - 7 所示。这些 角度是相对于垂直于表面的线(即 法线 ( normal )) 来测量的。(垂直于表面的线经常简单地表 示为“法线”,这样就可以说“入射角是相对于法线度量的”。)入射光线、反射光线和法线在同 一个平面中。 如果表面是光滑的,在相同区域照射到表面的平行光线(如那些来自同一光源的光线)就 会以相同的方向反射,并作为平行光线离开物体。这些反射光称 为镜面反射光 (specular light ) 0 10.4 渲染 315 注意,只有当表面和光源使得光反射到观察者的方向上,他才能观察到镜面反射光。它通常显 示为表面上明亮的高亮区。而且,因为镜面反射光与表面的接触时间最短,这使它非常接近原 始光源的颜色。 但是,物体表面很少是绝对光滑的,因此许多光线在表面照射点的方向会与大多数普通表 面的不同。而且,光线经常穿透表面邻接的边界,在表面的粒子间跳弹,最后作为反射光线离 开。结果是许多光线将向不同的方向散开。这种散开的光称为散射光 (diffuse light ) 0 与镜面反 射光不同,散射光在一定范围的方向内是可见的。此外,散射光与表面的接触时间长,更容易 受材料吸收特性的影响,因此它更接近物体的颜色。 图 10-8 表示了一个被单个光源照射的球,球上明亮的高亮区是镜面反射光产生的,通过散 射光,可以看见面向光源的半球的其他部分。注意,球面背着主光源的半球通过从光源直接被 反射的光是不可见的。球的这部分能够被看见是由于环境光 ( ambientlight ) 的存在,它是“漂 泊”或散幵的光,不与任何特定的光源或方向相关联。被环境光照射的表面部分经常显示为统 一的深色。 图 10-8 镜面反射光与散射光 大多数物体表面既反射镜面反射光又反射散射光,表面的特征决定了镜面反射光和散射光 的比例。平滑表面看起来发亮的原因在于,它们反射的镜面反射光多于散 射光; 粗糙表面看起 来为发暗是因为它们反射的散射光多于镜面反射光。.而且,由于某些表面具有细微的特性,入 射光的方向不同,镜面反射光和散射光的比例也会有所不同,从一个方向入射的光照射到这样 的表面上可能反射的主要是镜面反射光,而从另一个方向入射的光照射到这个表面上反射的主 要是散射光。因此,当它旋转时,表面的外观将从明亮变化为灰暗。这种表面称为各向异性表 面 (anisotropic surface ), 不同于 各向同性表面 (isotropic surface ), 后者的反射模式是对称的。 各向异性表面的例子可以在织物(如缎子)中找到,其中织物的细毛根据它的朝向改变材料的 外观。另外一个例子是运动场的草地表面,那里草的排列方向(通常是由草被裁剪的方式决定 的)产生了各向异性视觉效果,如同明暗相间的条纹图案。 2. 折射 1 现在考虑这种情况:光照射到透明的物体上,而非遮光的物体上。在此种情况下,光线是 穿过物体而并非从其表面反射出去。当光线穿透表面时,它们的方向改变 j , 这种现象 称为折 射 ( refraction ), 如图 10-9 所示。 折射程度是由相关材料的折射率决定的。折射率与材料的密度 有关。高密度材料往往比低密度材料具有更高的折射率。当光线进入折射率更髙的材料中时(如 从空气进入水中),它在入射点处向靠近法线的方向 弯曲; 如果光线进入到折射率较低的材料中, 它将向远离法线的方向弯曲。 316 第 10 章计算机图形学 材料间的边界 图 10-9 折射光 为了准确地渲染透明物体,渲染软件必须知道相关材料的折射率,但这还不够,渲染软件 还必须知道物体表面的哪一边表示物体的里面,而另一边为外面。光线是进入物体还是离开物 体?获得这些信息的技术有时相当巧妙。例如,如果规定,从外部观察物体的时候,总是按逆 时针顺序将多边形网格中多边形的顶点依次存放在一个列表中,那么通过给出的列表,我们可 以很容易地得知面片的哪一边表示物体的外面。 10 A 2 裁剪、扫描转换和隐藏面的消除 现在着重考虑从场景图生成图像的过程。目前使用的方法正是在大多数交互式视频游戏系 统中使用的技术。综合应用这些技术,形成了一个效果较好的方法,称为渲染流水线 (rendering pipeline ) o 在本节的末尾我们将介绍这种方法的一些优缺点,在 10.5 节将探讨两种候选的方法。 值得一提的是,渲染流水线处理不透明物体非常有效,因此折射就不是问题了。而且,它忽略 了物体间的相互影响,因此现在我们就不必担心镜像和阴影问题。 渲染流水线首先确定包含相机能“看到”的物体(或部分物体)的三维场景中的区域。这 个区域称为视体 (view volume ), 它是锥体内的一个空间,这个锥体是由从投影中心出发向图 像窗口边界延伸的直线所定义的(如图 10-10 所示)。 投影中心 只有视体内的物体部分 才会显示在图像窗口中 图 1 CM 0 确定在视体里面的场景区域 10.4 渲染 317 视体被确定下来以后,接下来就不用考虑那些与视体不相交的物体或物体部分了。毕竟, 那部分的场景投影将落在图像窗口的外面,因此不会出现在最终的图像中。第一步就是去除完 全在视体外面的物体。为了能够以流水线的形式进行处理,可以将场景图看作一个树型结构, 其中处在场景不同区域的物体被存储在不同的分支上。因此,仅需忽略树中的整个分支,就可 以去除大部分的场景图。 你是否注意到条纹衬衫和领带在电视屏幕上会出现奇怪的“闪光”?这是一个称为走样 ( aliasing ) 的现象产生的结杲,当所期望的图像网格中的模式与组成图像的像素密度不匹配 时,就会产生走样现象。例如,假设图像的一部分由黑白相间的条纹构成,但是所有像素的 中心碰巧落在黑色条纹上,那么物体将被当作全黑色的来渲染。但是,如果物体稍微移动一 下, 所有像素的中心可能都落在白色条纹上,这样物体将会突然变成白色的。有多种方法可 以改善这种令人心烦的效果。其中一种就是使用图像小区域的均值而不是精确的单个点来渲 染每一个像素3 C*-"ill?' ‘W 1'^ Hiy I' .y] I|* L ■ 匕 f > 1 : IJ>-^I'|J'01^L : .-o -\r- 复习题 1. 下列哪个是 2 D 图形学的应用,哪个是 3 D 图形 学的应用? a . 设计杂志页面的布局。 b . 用微软绘图工具绘制图像。 C. 为视频游戏从虚拟世界中生成图像^ 2. 3 D 图形学中的哪些术语对应于下列传统摄影 领域的术语?请解释你的答案。 a . 胶卷。 b . 取景器中的矩形。 c . 被拍摄的场景。 复习题 327 3. 当使用透视投影时,场景中的球在什么条件下 不会在投影平面上产生一个圆圈? 4. 当使用透视投影时,直线段的图像能变成投影 平面上的曲线段吗?证明你的答案。 5. 假设 8 英尺 ® 直柱的一端距离投影中心 4 英尺, 而且假设从投影中心到直柱一端的直线与投 影平面相交于一点,这一点距离投影中心1英 尺,如果柱体与投影平面平行,那么在投影平 面里柱子的图像长度是多少? 6. 说明平行投影和透视投影之间的区别。 7. 说明图像窗口和帧缓冲区之间的关系。 8. 应用 3 D 图形学产生电影和应用 3 D 图形学产生 交互式视频游戏的动画,二者之间有什么明显 的不同? 9. 说出物体的一些特性,这些特性可能出现在 3 D 图形场景使用的这个物体的模型中,说出 可能不会出现在模型中的一些特性,并解释你 的答案。 10. 说出一些未被只含多边形网格模型捕获的物 体的物理特性。(这样,单独的多边形网格没 有构建一个完整的物体模型。)如何把这些物 理特性中的一个添加到模型中? 11. 三维空间中的任意 4 个点能是多边形网格中的 面片的顶点吗?解释你的答案。 12. 下面的每个集合都表示了一个多边形网格中 面片的顶点(使用传统的直角坐标系),描述 网格的形状。 面片 1 : ( 0 , 0 , 0 ) ( 0 , 2 , 0 ) ( 2 , 2 , 0 ) ( 2 , 0 , 0 ) 面片 2: (0,0,0) (1/1,1) (2,0,0) 面片 3: (2,0,0) (1,1,1) (2,2,0) 面片 4: (2,2,0) (1,1,1) (0,2,0) 面片 5: (0,2,0) (1,1,1) (0,0,0) 13. 下面的每个集合都表示了一个多边形网格中 面片的顶点(使用传统的直角坐标系),描述 网格的形状。 面片 1: (0,0,0) (0,4,0) (2,4,0) ( 2 , 0 , 0 ) ■ 面片 2: (0,0,0) (0,4,0) (1,4,1) ( 1 , 0 , 1 ) 面片 3: (2,0,0) (1,0,1) (1,4,1) (2,4,0) 面片4: ( 0 , 0 , 0 ) ( 1 , 0 , 1 ) ( 2 , 0 , 0 ) 面片 5: (2,4,0) (1,4,1) (0,4,0) 14. 设计表示长方体的多边形网格,使用传统的直 角坐标系来表示顶点,绘制出一幅草图表示你 的解决方案。 15. 使用不超过8个三角片,设计多边形网格,去 近似表示半径为1的球的形状。(只有8个面片, 你的网格将是非常粗糙的近似球形,但目的是 帮助你理解什么是多边形网格,而不是产生球 的精确表示。)使用传统的直角坐标系表示面 片的顶点,绘制出网格的草图。 16. 为什么下面4个点不是平面片的顶点?. (0,0,0) (1,0,0) ( 0 , 1 , 0 ) ( 0 , 0 , 1 ) 17. 假设顶点(1,0,0)、(1,1,1)和 ( l s 0,2) 是平面 片的顶点,下面哪个线段是面片表面的法线? a . 从(1,0,0)到(1,1,0)的线段 b . 从 (1,1,1) 到 (2,1,1) 的线段 c •从(1,0,2)到 (0,0,2) 的线段 d . 从(1,0,0)到(1,1,1)的线段 18. 说出程序化模型的两种“类型”。 19. 在建模过程和渲染过程之间,哪一个更是 a . 标准化任务? b . 以计算为主的任务? c . 创造性任务? 证明你的答案。 20. 下列哪一个可能在场景图中表示? a . 光源 b . 不动的道具 o . 人物/演员 d . 相机 21. 在何种意义上说场景图的创建是 3 D 图形学处 理的关键步骤? 22. 场景图中的相机可能改变位置和朝向,这个问 题引入了何种复杂性? 23. 假设带有顶点(0,0,0)、(0,2,0)、 (2,2,0) 和(2,0,0)的平面片是平滑且有光泽的。如果 一条从点(0,0,1)发出的光线,在(1,1,0)处 入射表面,反射光将经过下列哪个点? a . (0,0,0 b . 0,1,1) c . (2,2,1) d . (3,3,1) ① 1 英尺 =0.3048 米。——编者注 328 第 10 章计算机图形学 24. 假设一个浮标上支撑着一个离静止水面高10 英尺的灯,如果有一观察者离浮标15英尺,离 水面高5英尺,在水面的哪个点上,观察者将 看到灯的反射? 25. 如果一条鱼在静止水面下游动,观察者从水面 上看鱼,从观察者的位置来说,鱼将显 示为: a . 在它的真实位置的上方且朝向背景 方向; b . 在真实的 位置; c . 在它的真实位置的下方且朝向前景方向。 26. 假设点 (1,0,0)、(1,1,1)和(1,0,2) 是平 面片的顶点,并且从物体外面观看时顶点是 以逆时针顺序依次排列。在每种情况下(从 物体外或物体内部观看),指明从所给出的 点发出的光线是否会与面片的表面相交。 a . (0,0,0) b . (2,0,0) c . (2,1,1) d . (3,2,1) 27. 举一个例子,说明视体外的物体能够出现在最 终图像中,并解释你的答案。 28. 描述 z 缓冲区的内容和用途。 29. 在针对隐藏面消除的讨论中,借助 z 缓冲区我 们描述了解决“前景/背景”问题的过程。用 第5章介绍的伪代码表示这个过程。 30. 假设物体的表面被交替的橙色和蓝色竖条 纹覆盖,每一个条纹的宽度都是1厘米,如果 这个目标被放置在场景中,这样像素的位置与 物体上的占据2厘米空间的点相关联,在最终 图像中,物体的可能显示是什么?解释你的 答案。 31. 虽然纹理映射和弹性映射都是与表面“纹理” 相关的方法,但它们是相当不同的技术,用一 小段话来对它们进行比较。 32. 列出渲染流水线的4个步骤,并给出简要说明。 33. 使用硬件/固件实现渲染流水线有哪些优点? 34. 设计为交互式视频游戏的计算机的硬件与通 用 PC 的硬件在哪些方面不同? 35. 传统渲染流水线的主要限制是什么? 36. 局部照明模式与全局照明模式之间的区别是 什么? 37. 光线踉踪与传统的渲染流水线相比有哪些优 点,有哪些缺点? 38. 分布式光线跟踪与传统的光线跟踪相比有哪 些优点,有哪些缺点? 39. 辐射度与传统的渲染流水线相比有哪些优点, 有哪些缺点? 40. 如果用传统光线跟踪生成的场景图像与用辐 射度生成的相同场景的类似图像相比较,两幅 图像有何异同? 41. 电影院放映的90分钟动画产品需要多少帧? 42. 描述粒子系统是如何被用来产生火苗闪烁的 动画的。 43. 当创建一个描述单个物体在场景中移动的动 画序列时,解释 z 缓冲区的使用有何帮助。 44. 当今的动画制作者与以前的动画制作者的工 作有哪些不同? 社会问题 下列问题有助于你分析一些与计算领域相关的道德、社会和法律问题。回答这些问题不是 唯一的目的,还应该考虑为什么这样回答,以及你的判断是否对每个问题都标准如一。 1- 假设计算机动画到达了在影视行业中不再需要真实演员的地步,那结果将是什么?不再 有“电影明星”会带来什么样的影响? 2. 随着数码相机和相关软件的发展,大众已经可以改变和制作照片。这将给社会带来何种 变化?会引起哪些道德和法律问题? , 3-照片所有权的程度是什么?假设一个人在网站上放置了他的照片,另一些人下载了这张 照片并修改了它,因此,主体处于妥协的状况,后来流行的是改变后的版本,照片的主 体应该拥有什么追索权? 4_帮助开发暴力视频游戏的程序员应该对此游戏产生的任何后果负有何种程度的责任? 是否应该限制儿童接触此类游戏?如果是的话,应如何限制以及由谁来限制?对社会上 其他一些特殊的团体(如罪犯),应该采取何种限制? 课外阅读 329 课外阅读 _ Angel , E . Interactive Computer Graphics, A Top-Down Approach Using OpenGL, 5 th ed . Boston , MA ; Addison - Wesley , 2009. Bowman , D . A., E . Kruijff , J . J . La Viola , Jr ., and I . Poupyrev . 3D User Interfaces Theory and Practice. Boston , MA : Addison - Wesley ,2005. Hill , Jr ., F . L . and S . Kelley . Computer Graphics Using OpenGL. 3 rd ed . Upper Saddle River , NJ : Prentice - Hall , 2007. McConnell , J . J . Computer Graphics, Theory into Practice. Sudbury , MA : Jones and Bartlett , 2006. Parent , R . Computer Animation, Algorithms and Techniques , 2 nd_ed San Francisco , CA : Morgan Kaufinann , 2008. 人工智能 t 章探讨计算机科学的一个 分支: 人工智能。尽管该领域仍然处于发展的初期阶段, 但它已经产生了一些令人惊讶的结果,例如,机器人象棋大师,用来学习和推理的 计算机,协调一致来完成一个共同的目标(如赢得一场足球比赛)的多个机器。在人工智能领 域,今天的科学想象也许在明天就会变成现实。 ' 本章内容 _____, 麵 __ U .2 :感知 幽 誠_赢1編 ..7= ■- .• • - , • ---- . . : STr . ii .5 人 4 神缘 网络: 娜 :: ipllpf:; 11.7 后:果的思考 編識 ...A 课外阅读 人工智能是计算机科学的一个领域,旨在设法建造自主的机器——无需人为干预就能完成 复杂任务的机器。这个目标要求计算机能够感知和推理,虽然这两种能力属于常识行为,对于 人脑来说是与生俱来的,但对于机器来说却是有困难的。结果是该领域的工作一直富于挑战性。 本章就来探讨这个广阔研究领域中的一些主题。 11.1 智能与机器 人工智能领域十分广阔,并且与其他学科相融合,如心理学、神经学、数学、语言学以及 电子与机械工程等学科。为了集中思绪,我们先来考虑智能体的概念以及智能体可能呈现的智 能行为类型。实际上,人工智能的许多研究都可按智能体的行为来分类。 11.1-1 智能体 智能体 ( agent ) 是对环境的刺激做出响应的一种“装置”。很自然地人们会把一个智能体 想象为一个像机器人这样的单个机器,尽管它可以有别的形式,如一架自动飞机、交互式视频 游戏里的一个角色,或是通过因特网通信的进程(可能作为客户机、服务器或对等机)。大多数 智能体具有传感器和效应器,前者接收来自环境的数据,后者对环境做出反应。常见的传感器 包括麦克风、摄像机、距离传感器以及空气或土壤采样设备等,效应器的例子有车轮、腿、翅 膀、夹子以及语音合成器。 很多人工智能的研究可以在构建智能体的环境中进行描述,这意味着智能体效应器的动作 必须对通过传感器接收的数据做出合理的响应。按照响应的级别,我们可以对这些研究进行 分类。 11.1 智能与机器 331 最简单的响应是映射行为,这只是对输入数据的一个预定的响应。更高级的响应用于获取 更智能的行为。例如,我们可以赋予智能体以环境知识,要求其相应地调整为。投掷棒球的 过程在很大程度上是映射行为,但决定怎样扔,向哪个方向扔,就需要对当前环境有一定的了 解(如此时一个人出局,跑手在一垒和三垒)。怎么样存储、更新和获取这种现实世界的知识, 然后最终应用到决策过程中,一直是人工智能领域具有挑战性的问题。 如果我们希望智能体的目标是赢得一场棋赛或是通过一条拥挤的通道,那么就需要另一种 层次的响应。这种有目标性的行为需要智能体的响应(或是一系列的响应)应当是周密考虑的 结果,构成一个行为计划,或是在当前的各种选项中选取最好的行为。 在有些情况,随着智能体不断学习,它的响应能够不断地得到改进。这可以采取不断发展 过程性知识 ( proceduralknowledge ) 的形式(学习“怎样 ”),或者储备陈述性知识 (declarative knowledge ) 的形式(学习“什么”)。学习过程性知识涉及一个反复试验的过程,在这个过程中, 智能体从出错受罚、正确受奖的过程中学习适当的反应 a 根据这个方法开发了一些智能体,它 们能够随着时间的推移逐步提高在跳棋和国际象棋等竞赛性游戏中的能力。学习陈述性知识通 常采取的形式是扩充或变更智能体的知识库里的“事实”。例如, 一 个棒球运动员必须不断地重 复调整其知识数据库(虽然还是一人出局,但现在跑垒手在第一垒和第二垒),从中对将来的事 件做出合理响应。 一个智能体要对刺激做出合理的响应,就必须“理解”由其传感器接收的刺激。也就是说, 智能体必须能够从其传感器产生的数据里提取信息,即智能体必须能够感知。有些情况下,这 是一个简单的过程。从一个陀螺仪获取的信号很容易就能编码成适合计算的形式,以确定响应。 但是有些情况下,从输入数据提取信息并不容易,例如理解语音和理解图像就很难。同样,智 能体也必须能够以与效应器兼容的方式表达它们的响应。这可以是一个简单的过程,也可能要 求智能体把其响应表达为一个完整的口语句子——这意味着智能体必须生成语音。所以,像图 像处理和分析、自然语言理解以及语音的生成这些主题都是重要的研究领域。 我们这里标识的智能体的属性既表示以前的研究范畴, 又表示当今的研究范畴。当然,它们彼此之间并不是完全无 关的。我们希望最终能够开发出处理所有这些属性的智能体, 产生出能够理解来自环境的数据,并通过学习过程开发新的 响应模式的智能体,学习的目的是最大限度地提髙智能体的 能力。然而,通过孤立各类推理行为并独立研究它们,研究 人员获得了一个立足点,由此入手,可以与其他领域的发展 图 11-1 8块拼图的最终布局 相结合,产生更加智能的智能体。 本节的最后我们介绍一个智能体,也为 11.2 节和 11.3 节 的讨论提供一个背景。该智能体是为解决八数码游戏 ( eight - puzzle ) 而设计的,该游戏由8个小方块组成,标号为 1〜8,放置在一个3行3列总共可容纳9个小方块的框架内(如 图 11-1 所示)。这样,框内的方块间有个空位,挨着空位的方 块可以移动,允许框内的方块随意排布。问题是要把杂乱排 布的方块移回到它们的初始位置(如图 11-1 所示)。 我们的智能体采用如图 11-2 所示的这样一种装置,该装 置配备有一个夹具、一个摄像机和一个带橡皮头的指杆,橡 皮是为了推移东西时不会打滑。当首次开启该智能体时,夹 具会一张一合,好像索要这个拼图一样。当我们把一个随意 图 11-2 解决八数码游戏的机器 332 第 11 章人工智能 排布的拼图放进夹具时,夹具就会把它夹住。不一会儿,机器的指杆降低,并开始在框架内推 移方块,直到所有的方块恢复到原始位置。这时,机器就放开拼板并自己关掉电源。 这个解决八数码游戏的机器展现了已经提到过的两个智能体的属性。第一,它必须能够感 知,就是必须从摄像机拍摄的图像中获取当前拼图的状态。我们将在节阐述理解图像的问 题。第二,它必须开发和实现一个达到目标的计划。这些问题将在 11.3 节中阐述。 11.1.2 研究方法 要对人工智能领域作出客观的评价,应该知道对该领域的研究存在两种路线。一种可以称 为工程线路,即研究人员侧重于开发展示智能行为的系统。另一种可以称为理论路线,即研究 人员会尝试从计算的角度来研究动物(尤其是人类)的智能。我们通过考虑这两种路线的执行 方式,来说明这种二分的研究方法。在工程路线指导下,由于潜在目标是生产出符合某种性能 目标的产品,因此就产生了面向性能的方法论;而在理论路线指导下,由于潜在目标是增进人类 对计算智能的理解,因此关注重点是底层处理而非外在性能,结果就产生了面向模拟的方法论。 作为一个例子,考虑自然语言处理和语言学领域。这些领域关系密切,各领域的研究可互 相取长补短,但它们的根本目标却不同。语言学家的兴趣在于弄明白人类如何处理语言,因此 更倾向于理论性的研究,而自然语言处理领域的研究者的兴趣在于开发能处理自然语言的机 器。所以,语言学家以面向模拟的模式运作,也就是建造用来检验理论的系统。相反,自然 语言处理的研究者以面向性能的模式运作,也就是建造执行任务的系统。后一种模式产生的系 统(如文档翻译机和响应口头命令的机器系统)在很大程度上依赖于语言学家获取的知识,但 在特定系统的限定环境中经常使用可以工作的“简化方法”。 作为一个基本的例子,考虑为一个操作系统开发一个外壳 ( shell ) 的任务,它通过口述英 语命令接收来自外部世界的指令。在这种情况下 , shell (-个智能体)不需要考虑完整的英语 语言。更准确地说,外壳不需要区分 copy 这个词的不同意思。(是名词还是动词?是否有抄袭的 含义?)相反,外壳仅仅需要把 copy 这个词与其他命令(如 rename 、 delete ) 区分开来就行。所 以外壳只要将输入与预先确定的音频模式相匹配,就能够执行它的任务。这种系统的性能可能 会令工程师满意,但从美学角度看,其实现方式也许并不能取悦理论家。 11-1-3 图灵测试 过去, 图灵测试 (Turing test , 1950年由阿兰•图灵提出)一直作为衡量人工智能领域发展 的一种标准。现在,图灵测试的重要性已不及从前,但它仍是人工智能领域重要的一部分。图 灵的提议是,允许一个人(我们称他为询问者)与一个测试对象通过一个打字机系统进行通信, 而没有告知询问者测试对象究竟是一个人还是一台机器。在这种环境中,如果询问者没能够把 一 台机器与一个人区分开来,那么可以说明这台机器的行为是智能的。图灵预测,到2000年, 机器将会有30%的机会通过一个5分钟的图灵测试——这个预见惊人地准确! 奇求建造能够糢仿人类行誇妁机器有很长的历史,不过很多人会火同现我入工智能领域起源 于1950年 。 彝在这一年,时兰> _焉本表 T 论冬 “ C 卿碑 ttag M 如姆卿紐 d 轉綱琴轉❿/損泮_ 器硫够呈本展现着能蝻存为。 :这 不颔域4 名寺一 一人工智能一一競 后由“ ▲ 卡锡 - ( John^McCartky ) 在一个现在看来颇具传奇色彩的建议里提出,他建议“195 许夏 天在达 特茅知学院 ( Dartmoufl^ollege ) 开展人工智 能的荷 究”,以探究“这种推测,卽认为认知的每个 方南 g 智轉的 © 何其他轉征原 — 上都锻够择精碑地揭迷,从 to 槪破剩__莫拇它 W 机寒 乂 11.2 感知 333 图灵测试已不再被认为是对智能的有效度量,其中一个原因在于,一个怪诞的智能显示可 以用相对简单的手 法生产 ( produce ) 出来。图灵测试场景的一个著名示例是20世纪60年代中期 由 Joseph Weizenb 叫 m 开发的程序 DOCTOR (更通用的系统版本称为 ELIZA h 这个父互程序被 设计用来反映指导心理治疗的罗杰斯心理分析的场景,计算机扮演了分析师的角色,而用户扮 演病人。在内部, DOCTOR 所做的一切是根据某些定义明确的规则将病人的陈述重新构造并反 馈给病人。例如,回应病人的陈述“我今天觉得很累”, DOCTOR 可能 回答: “为什么你今天觉 得很累?”如果 DOCTOR 不能识别句子结构,它仅仅作出像“继续”或者“这很有趣”这样的 响应。 Weizenbaum 开发 DOCTOR 的目的是为了研究自然语言的交流。心理疗法的目标仅仅提供了 一个程序可以“交流”的环境。然而, Weizenbaum 没有想到的是,个别的心理学家建议把3&个 程序用在实际的心理治疗中。(罗杰斯的论点是,在治疗期间,应该是病人来主导对话,而不是 分析师。所以他们认为计算机也能像治疗师那样引导对话。)而且, DOCTOR 表现出极强的理解 能力,以至许多与它“沟通”过的人会迎合这种与机器的问答式对话。在某种意义上 , DOCTOR 通过了图灵测试。其结果是,在伦理及技术上都产生了争议, Weizenbaum 成为一位在技术进步 的世界中维护人类尊严的倡导者。 较新的图灵测试“成功”的例子包括一些因特网病毒,为了诱骗人类放松对恶意软件的防 护,它与人类受害者进行“智能”对话。此外,与图灵测试类似的现象发生在下象棋等计算机 游戏的场景中。尽管这些程序选择棋路时仅仅通过应用蛮力技术(这与我们将在 11.3 节讨论的 内容相似),但人类在同计算机的竞赛过程中常感觉机器拥有创造力甚至个性。相似的感觉发生 在机器人技术领域,根据物理属性建造的机器表现出了智能的特征。例如,玩具机器狗仅仅通 过点头或者是竖耳朵来响应声音,就表现出了可爱的个性。 讎麵 _圍 1 |:::: : ..戀 1 ■指二 t 智熊体苛他几神“智能”劾作。 ^ ' 乂 :' .1 2. 一稞_只有一東^源:的搪室 m 的:楫物,::僉朝•光源穷向樂长 〈这 是一神晌^ t ?! 植减拥# 替能 圍議議議議議 3 . 輝缉十台售赞搪癣撩的镔钮爱售不同的•品,你认为違样的一旮齊“細道个姨钮? 4 . .如,」娜器違过罵禪试,砵同埤台椒轉智能的辱?鄭柄舉,释每召如词看上未 議義:_____ 1 麵窗_靈 議囊 Billi 國 iiH 議!議議 臟^ „ ; 如巣;你稍辰发領嗔垮二^桃蠢鑛读,,喊翁认 11.2 感知 一个智能体要智能地响应从它的传感器接收的输入,就必须能够理解输入。也就是说,智 能体必须能够感知。本节我们来探讨感知的两个研究领域,即理解图像和理解语言,这两个领 域被证明特别具有挑战性。 11-2.1 理解图像 让我们考虑 1 U 节介绍的解决八数码游戏的机器所提出的问题。机器上夹具的张合没有表 334 第 11 章人工智能 _ 现出严重的障碍,在这个张合的过程中,因为这里的应用要求的精度不高,所以检测夹具中是 否有拼图的功能也很简单,即使是摄像机对拼图的对焦问题也不难解决,方法是通过设计夹具 把拼图安置在一个事先确定的位置,以便观察。所以,机器需要的第一个智能行为是从视觉媒 介中提取信息。 必须认识到,机器在看拼图时所面对的问题不单是产生和存储一张图像,这方面的技术很 多年前在传统的摄影及电视系统中就能做到。相反,这里的问题是为了提取拼图当前的状态, 要理解这个图像(可能随后还要监控方块的移动)。 在八数码游戏机器的案例中,对拼图图像的可能解释是相对有限的。我们可以假定,在一 个排列好的模式里所呈现的总是一幅包含数字1〜8的图像。问题只是去提取这些数字的排列。为 此,我们想象拼图的图像已经在计算机内存中,按照位进行编码。编码中的每一位表示具体像 素的亮度。假定图像的大小统一(机器把拼图放在摄像机前的预定位置),把图像的不同部分与 由位模式构成的预定模板相比较(位模式是由拼图中使用的单个数字生成的),机器就能够检测 出哪个方块在哪个位置。如果发现匹配,则说明拼图达到了要求的条件。 这种图像识别技术是光学字符阅读器中使用的一种方法。但它是有缺点的,它对被读的符 号在类型、大小以及方位上要求一定程度的一致性。特别是,即使对于相同的符号,外形也相 同,但字体较大的字符产生的位模式与较小字体的模板也不匹配。此外,可以想象,当试图处 理手写材料时,问题会变得非常困难。 解决字符识别问题的另一个方法基于匹配几何特征,而不是符号的精确外形。在这种情况 下,数字1表示为一条单竖线,数字2可能代表一条不封闭的曲线,底部与一条水平直线相连, 等等。这种符号识别的方法分 两步: 第一步是从要处理的图像中提取图像特征,第二步是把这 些特征与已知符号的特征进行比较。与模式匹配方法一样,这种符号识别技术并不可靠。例如, 图像的少许错误会产生一组完全不同的几何特征,比如区分字母 O 和 C , 以及八数码游戏里的数 字3和8时就容易混淆。 幸好在八数码游戏中不需要理解一般的三维图像。例如,我们的优势是能保证识别的形状 (数字1〜 8) 相互孤立地处在图像上的不同部分,而不是常见的重叠图像。例如,在一张普通的 照片中,我们面临的不仅是从不同的角度识别一个对象的问题,还包括被遮蔽的对象的某些 部分。 理解一般图像的任务通常采取两步进行处理: (1) 图像处理 ( imageprocessing ), 指标识图 像的 特征; (2) 图像分析 (image analysis ), 指理解这些特征代表什么意思的过程。在利用符号 的几何特征识别符号的描述中,我们已经提出了二分的处理方法。在这个过程中,图像处理由 标识在图像中发现的几何特征的过程来表示,图像分析由标识那些特征的含义的过程来表示。 图像处理带来了大量的研究课题。一个是轮廓增强,使用数学技术使图像中区域间的边 界线变得更清晰。在某种意义上,轮廓增强试图将照片转换成线条画。图像分析的另一个活 动是区域查找。这是标识图像中区域的过程,这些区域拥有共同的属性,比如亮度、颜色或 者纹理。这样的一个区域很可能代表图像中某个对象的一部分。(这种识别区域的能力使得计算 机可以给老式的黑白电影着上彩色。)图像处理领域还包含另一个活动——平滑 ( Smoothing ), 即去除图像中的缺陷。平滑使图像中存在的错误不会混淆其他图像处理步骤,但是过度平滑 会导致重要信息的丢失。 平滑、轮廓增强以及区域发现都是用来标识图像中各种成分的步骤。图像分析是确定这些 成分代表什么,以及最终确定这个图像代表什么的过程。这里我们还要面对从不同视角识别被 部分遮挡的对象这样的问题。图像分析的一种方法是首先假定一个图像大概是什么,然后尝试 把图像中的成分与那些猜测的对象相联系。这看起来是人类所使用的方法。例如,有时我们会 11.2 感知 335 发现,在我们视觉模糊的情况下识别一个没有想到的对象是很困难的,但是一旦有一个该对象 可能是什么的线索,我们就能容易地认出它。 与一般图像分析相关的问题特别多,在这个领域还有许多研究要做。图像分析这个领域 证明,那些人类智能能够很快又非常容易完成的任务,却对机器能力提出了强有力的挑战。 :也 古 \ j »■> a ,:入 能勢濟_挑器进行编_溱典其臂維棉炎的刻錄龙癥人_能1_吐 今 禾在不同雇壤 J ; 已 备故土众所接受桎是 ,机# ,龜 过祕無豳获 轉智力,,:亦.即意 说的邦猶推测 鏃力、 瘌感 k 为辫虽 A 本攀雜 (: stipiig;AI X :智施别琴逆疒泛的令论 。:乂 砷者缽抓器 在本廣 毒与乂彝采 I %永号不猶參人 类那样 感::受義:觀断讀^接” 4氧考廣 lr 我,:-而:,玄持者认为入入类.的染脑是街赛多小,:崎部 ft 构 gr 每平部 ,舞本 14 ^谏有意 识,:,但是当它们举合:在^:起就成十:人。俾们辨称,为升么同:彝的视,象就.本可雔法 氣在 机箨身 ...; : .':•.'•:: : !'••':-:!• !•'!':, :'^: 1 ; -:':(; .i- : •; ••••:':., :'-,; ••'; .':,';• '• !:'•:••••':• : : ':••,::. i,"'L :,/. :':!;' •':•:'"' h :,'-,; ,' _:. ; .X. ::; -•;-;•'•,: •:•-, .-:•-•;•:•: ::•-••. : •; •; :'••': : .■.■■■•■. - •":':.:•':'::': •'-•,! : : •:• :: ! -: : . ! : •; : '.;;;;!: | ;|:.. | |' •;;. :i. A .::.. ':■•:••,•:•"'!: •,• .; - r , •,:!; i'.vr-,- •'.!:• : : 'v:--i'-''H'i '• ! •!' : ': | lj :: : . 仏騎…:人 ;' \ 二 、:' '■ ' ::麵决彈人工44#轉鈐#养在予:, 泰能; 和意识这样的 •錢 4在辞性.,不能够瘤4鉢1 兵:如鲫 i •姻卑指::出:喊砰样;:屬 人涔 :其他人属于有智賊的是卸寿他杨的抒羝表樣出智/ 能^卽俠我 ft 不:_蹀 察到考 始:内伴#智力状:狂那47如果 祇器也 皇说外在 的意识 梯裇, 我们 灰杏 准备心 c 机舊具备同祥蝻 | 永准呢?.为什么是,为什么本是? 11.2.2 语言处理 理解语言是感知问题的另一个已证明的颇具挑战性的问题。把形式化的高级程序设计 语言翻译成机器语言(参见 6.4 节)获得的成功使早期的研究人员认为,通过编程使计算机 具有理解自然语言的能力将在几年后变成现实。实际上,翻译程序的这种能力给我们一种错 觉'~■机器真能理解被翻译的语言。(回忆 6.1 节中 Grace Hopper 讲的故事,那个经理以为她正 在教计算机理解德语。) 这些研究人员没有明白形式化的程序设计语言与英语、德语以及拉丁语这些自然语言之间 在深度上的差异。程序设计语言由精心设计的原语组成,每个语句只有一种语法结构,只有一 种意思。相反,自然语言的一个语句会因为上下文的不同,甚至交流方式不同而有多种意思。 因此,人类理解自然语言很大程度上依靠额外的知识。 例如,句子 以及 Norman Rockwell painted people . Cinderella had a ball . 都有多种意思,但通过语法分析或单独翻译每个词并不能区分这些意思。实际上,要理解这些 句子需要有理解句子上下文的能力。在有些场合, 一 个句子的真实意思与它的字面意思完全不 同。例如,“你知道几点了吗? ”通常的意 思是: “请告诉我现在几点了。”或者如果说话者已经 等候了很长时间,那么这句话意思可能是:“你来得太迟了。” 要弄明白一种自然语言中的一个句子的意思需要几个层次的分析。第一层是语 法分析 (syntactic analysis ), 其主要成分是句法分析。在这里,句子 Mary gave John a birthday card .( 玛丽给约翰一张生日贺卡。) 336 第 11 章人工智能 的主语是 Mary , 而句子 John got a birthday card from Mary . ( 约翰收到玛丽赠送的一张生日贺卡。) 的主语是 John 。 分析的第二层次称为语义分析 (semantic analysis )。 语法分析仅标识每个词在语法上的作用, 与语法分析不同,语义分析的任务是标识句子中的每个词在语义上的作用。语义分析试图标识 这样的内容,如描述的动作、动作的主体(可能是句子的主语,也可能不是)以及动作的目标。 正是通过语义分析,我们认为句子“玛丽给约翰一张生日贺卡”和“约翰收到玛丽赠送的一张 生曰贺卡”是在说同一件事情。 分析的第三层是上下文分析 (contextual analysis ) 。在这一层,句子的上下文被引入理解过 程中。例如,很容易分辨出句子 The bat fell to the ground . 中的每一个单词在语法上的作用。我们甚至能通过识别动作 “ falling ” 和动作主体 “ bat ” 等来 实现语义分析。但只有等我们考虑到上下文后,句子的意思才能变得明确。尤其是,这句话在 棒球比赛这样的背景下和在洞穴中探险这样的背景下有着不同的意思而且,只有在上下文这 一层,问题“你知道几点了?”的真正意思才能最终揭晓。 我们应当注意,各个层次分析(语法、语义及上下文)并不一定相互独立。对于句子 Stampeding cattle can be dangerous . 如果我们想象是一群牛在惊跑,那么主语是名词 cattle (用形容词 stampeding 力卩以修饰)。但是如 果语境是哪个恶作剧者以惊吓牛群取乐,那么主语就是动名词 stampeding (宾语是 cattle )。 因此, 这个句子不只有一个语法结构*—究竟哪一个意思正确要依赖于上下文。 自然语言处理的另一个研究领域涉及整个文档,而不是单个句子。这 里涉及 的问题可分成 两类:信息检索 (information retrieval ) 和信息提取 (information extraction ) 0 信息检索的任务 是标识与手头论题有关的文档。例如万维网的用户试图找到与特定主题相关的站点时所面对的 问题。该技术的当前状态是为关键字搜索站点,但这经常产生大量的错误链接,并且由于其处 理 “ automobiles ” 而不是 “ cars ”, 它会忽略一个重要的站点。需要的就是能理解所考虑站点内 容的搜索机制。获取这样的理解是很困难的,这也就是许多搜索机制都转向釆用像在 4.3 节介绍 的 XML 这样的技术,来产生语义网 Web 的原因。 信息提取是指从文档中提取信息这样的任务,并采用一种形式以方便用于其他应用程序。 这个意思可以理解为为一个特定的问题确定答案,或者是将信息以某种格式记录,以备日后 解答问题时使用。有一种这样的格式称作框架 ( frame ) ,框架实质上是一种记录细节的模板。 例如,考虑一个读报系统,该系统可以利用各种各样的框架,报纸上的每一类文章用一个。 如果系统认定一篇文章是关于入室盗窃的报道,它会继续试图把这填写到入室盗窃框架中, 该框架可能要求填写这样一些条目,如失窃地点、失窃时间和日期以及失窃物品等。相反, 如果系统认定一篇文章是关于自然灾害的报道,那么它就填写自然灾害框架,引导系统确定 灾害类型、损失的大小等。 信息提取者记录信息的另一种形式称为语义网 (semantic net )。 这实质上是一个大的链式数 据结构,结构中的指针用来指不数据项之间的联系。图 11-3 显示了一■个语义网的一部分,突出 ①英文单词 bat 有蝙蝠和球棒两种意思,在棒球比赛中应指“球棒”,在洞穴中探险时则可能指“蝙蝠”。 译者注 11.2 感知 337 显示的部分是从句子 Mary hit John. 中得到的信息。 从句子 Mary hit John . 中得到的信息 图 11-3 —个语义网 338 3. 下图中有 凡 个方块?怎样对一个机器编程使其能正确回 答这平 问:题? 4:你―如荷知道句子 “Nothing is better than coti^lete happiness ” 和句子 A bow } of cold soup is better than nothing ” 不是暗指 “A bowl of cold soup is better than complete happiness ” ? 你这种区分能力如何被移 植到一台机器上? 5. 指出在翻译句子 “ They 締 racing horses ” 时的二 义性。 6. 比较下列两个句子的语法分析结果。然后解释二者在语义上的不周。 The farmer built the fence in the field . The fefmer built the fence in the winter . 7 . 根 据厨 lf -3 中的语义两和 John 之间是什么家庭关系?; _ , ' :' : 11.3 推理 __ 现在,让我们来利用 ii . i 节介绍的求解八数码游戏的机器来探讨开发具有基本推理能力的 智能体的技术。 11.3.1 产生式系统 解决八数码游戏的机器从看到的图像中解读出了方块的位置以后,它的任务就变成了决定 需要哪些移动来求解难题。马上可以想到的一个方法是,把方块的所有可能排列对应的解决方 案都预先编写到机器中。然后,机器的任务就只是选择和执行合适的程序。然而,这个八数码 游戏有100 000多种 布局, 所以对每一种布局提供一个直接的解决方法的方案显然不可取。因此, 我们的目标是对机器编程,让机器能够自己构建难题的解决方法。也就是说,必须对机器编程 使其能够实现基本的推理活动。 开发机器的推理能力己经是一个研究多年的主题。有关这方面的研究已经达成一个共识, 即有一大类推理问题具有共性,这些共性被单独放在一个抽象的实体中,该实体 称为产生式系 统 (production system )。 这种系统由三个主要部分组成。 (1) 状态集合。每 个状态 ( state ) 是一个可能在应用环境中发生的情形。最初的状态 称作开 始状态 ( start state ) (或者初始状态),期望的状态 称作目标状态 (goal state )。 (在我们的案例中, 一个状态就是指八数码游戏的一种布局,开始状态就是八数码游戏提交时的布局,目标状态就 是图 11-1 所示的已经解决了难题的布局。) (2) 产生式集合(又称规则或者移动)。 产生式 ( production ) 是指能在应用环境中执行 的一个操作,以使系统从一个状态转移到另一个状态。每个产生式可以与一些先决条件相 关联,也就是说,在应用产生式之前,环境中必定会出现一些可能存在的条件。(在我们 的案例中,产生式就是方块的移动。一个方块每次移动的前提条件是其相邻的位置必须有 空位。) (3) 控制系统。 控制系统 (control system ) 是由解决问题使其从开始状态变换到目标状态的 逻辑组成的。在处理过程的每一步,控制系统都要决定,在满足先决条件的那些产生式中,下 一步该执行哪一个。(对于八数码游戏的例子,给定一个特定状态,在空位旁会有几个方块,因 而存在几个可用的产生式。控制系统必须决定移动哪一 1 个方块。) 11.3 推理 339 注意,在一个产生式系统的上下文中,赋予解决八数码游戏的机器的任务可以被公式化。 在这种情况下,控制系统采用程序的形式。该程序检查八数码游戏的当前状态,确定实现目标 状态的一系列产生式,并执行这一系列产生式。因此,我们的任务就是为解决八数码游戏设计 一个控制系统。 控制系统开发中的一个重要概念是问 题空间 (problem space), 它是产生式系统中的所有状 态、产生式以及先决条件的集合。问题空间通常概念化成 状态图 ( stategraph) 的形式。这里, 图这个词是指一种数学家称为 有向图 (directed graph) 的结构,是指一组由箭头连接起来的称 为节点 ( node) 的位置。 一 个状态图由一组用箭头连接的节点组成,节点表示系统中的状态, 箭头表示从一个状态转换到另一个状态的产生式。状态图中两个节点被一个箭头连接的条 件是: 当且仅当有一个产生式,它把系统从箭头起点处的状态转换到箭头终端处的状态。 我们应当强调的是,正像在解决八数码游戏时拼图可能状态的数量使我们难以明确地提供 预先编写好的解决方案一样,数量太大的问题也使得我们不能明确地表示整个状态图。所以, 状态图是概念化手头问题的一种方法,但不能用来表示全部内容。虽然如此,你会发现,考虑 图 11-4 显示的八数码游戏的一小部分状态图对于求解难题是有帮助的。 当根据状态图来考虑时,控制系统面临的问题变成了寻找一连串从开始状态导向目标状态 的箭头。毕竟,这一连串的箭头代表了解决初始问题的一系列产生式。所以,不管应用是什么, 控制系统的任务都可以看做是寻找一条贯穿状态图的路径。对控制系统的这种普遍观点是根据 产生式系统对要求推理的问题进行分析的成果。如果一个问题能够根据产生式系统来描绘,那 么它的解决方法就能够根据搜索一条路径来明确表达。 图 114 八数码游戏状态图的一小部分 为了强调这一点,我们先来考虑其他任务是如何接照产生式系统来设计,然后在控制系统 通过状态图发现路径的背景下完成的。人工智能的经典问题之一就是下象棋这样的游戏,这类 游戏在一个明确规定的背景下属于中等复杂度,因此,它为理论测试提供了一个理想的环境。 在象棋游戏中,基本产生式系统的状态是可能的棋盘布局,产生式是棋子的移动,控制系统就 具体为棋手(人或别的)。状态图的开始节点表示棋子在初始位置时的棋盘。从该节点出发的分 340 第 11 章 人工智能 支是一些箭头,这些箭头指向游戏中祺子第一步移动之后会形成的那些棋盘布局,而从这些节 点出发的每一个分支又指向下一步移动棋子会形成的那些布局,依次类推。通过这种明确的表 达,我们可以把一个象棋游戏想象为由两个选手组成,每个选手都试图通过在一个大的状态图 中寻找一条通向自己选择的目标节点的路径。 或许从给定事实得出逻辑推论的问题是一个不太明显的产生式系统的例子。在这种情况下, 产生式是称为推 理法则 ( inferencerule ) 的逻辑规则,这些规则允许从旧命题中形成新命题。例 如,命题“所有超级英雄都是崇高的”和“超人是超级英雄”可以合并产生“超人是崇高的”。 这样一个系统中的状态由各种命题组成,这些命题在推导过程的一些特定点 为真: 开始状态是 基本命题(常称为公理)的集合,从中可以得出 结论; 而目标状态则是包含了所提出结论的命 题的任意集合。 例如,图 11-5 显示了以下推论所经历的状态图的一部分。从一组命题“苏格拉底是男人”、 “所有男人都是人”及“所有人都终有一死”可以推出“苏格拉底终有一死”。从中我们看到, 随着推理过程应用合适的产生式来生成新的命题,知识的主体从一个状态转换到了另一个状态。 当今,这种推理系统经常应用在逻辑程序设计语言 (6.7 节)中,它是大 多数专家系统 (expert systems ) 的核心。专家系统是为模拟因果推理而设计的软件包,当人类专家面对相同的情形时 将遵从这些因果推理。例如,医疗专家系统用来协助疾病诊断或改良治疗。 开始状态 11.3.2 搜索树 我们已经看到,在产生式系统的环境中,控制系统的工作涉及搜索状态图,找出从开始节 点到目标节点的一条路径。完成这种搜索的一个简单的方法就是仔细观察每一个从初始状态发 出的箭头,并记录下每一个目标状态,然后继续仔细观察从这些新状态发出的箭头,再记录下. 结果,依次类推。对目标的搜索像向水中滴入一滴墨水一样,从开始状态扩散开来。这个过程 继续进行,直到一个新状态就是目标状态,在这里,解决方法就找到了,控制系统只需要沿着 找到的这条从开始状态到目标状态的路径应用产生式即可。 11.3 推理 341 135 135 1 5 26 46 436 478 728 728 35 135 35 135 15 15 126 2 6 146 746 436 436 478 478 728 28 728 728 415 152 135 3 5 32 43 742 142 786 786 8 6 786 415 415 152 152 135 135 345 35 3 2 732 4 3 436 7 2 742 1 2 142 786 86 786 78 846 86 786 786 135 46 728 13 135 465 468 728 72 135 135 123 123 123 413 82 48 45 485 45 25 476 762 786 7 6 786 786 135 35 135 13 123 23 123 123123 12 413 41: ^ 溫以餵 7 能谥彳! 54 ?總镏 7 iiW 这种策略的结果实际上是建立一棵树,称作 搜索树 (search tree ), 它由被控制系统分析后 得到的部分状态图构成。搜索树的根节点是开始状态,每个节点的子节点由那些应用一个产生 式从父节点可到达的状态 构成。 搜索树中节点间的每条线段代表一个产生式的应用,从根到叶 子的每一条路径代表状态图中相应状态间的一条 路径。 解决图 11-6 所示布局的八数码游戏将会产生的搜索 树如图 11-7 所示。该搜索树最左边的分支代表试图通过最 初向上移动方块6来解决问题,中间分支表示向右移动方 块2的开局方法,而最右边的分支表示以向下移动方块5 来开局。搜索树进一步显示,如果以向上移动方块6来开 图 11-6 —个尚未解决的八数码游戏 局,那么下一个允许的产生式只能是方块8右移。(实际 ’ 上,这时还可以将方块6向下移动,但这仅仅是上一个产 生式的倒置,因而是毫无意义的移动。) ' 135 42 786 : 7-> 1: : 卜: H- O. ‘卜 L 卜 +rr: .i .... :l /V 1 J3, 1 - H l-H l-H VTW 丨 1 1 Hrr 2 ■ ' 7入 ,V.'M r ' r '-■VrKH :: : t : y'r , '•1 h :• ,: M,, '■ l| _ .1. 图 11- 7 搜索树的一个示例 目标 目标状态出现在图 11-7 显示的搜索树的最下面一 层。因为这预示了已经找到了一个解决方法,所以控制 系统可以结束搜索过程并开始构建指令序列,该指令序 列将用来解决外部环境中的拼图难题。实际上是一个简 单的过程:从目标节点的位置上行,同时在遇到由树枝 线表示的产生式时将它们压入栈。将这种技术应用到图 11-7 所示的搜索树中,产生了如图 U -8 中所示的产生式 桟。现在,只要把指令从该栈中弹出并执行,控制系统 就能够解决外界的问题。 栈顶 f!’ 卜-- .卜乂 A J .-. 卜 M '.jr Ai,|i i "iVi : i.; 1 ... ^:__沿 :?: 图 11-8 压入栈的产生式以备以后执行 526 526 348 >48 17 526 .7 526 38 147 .-•-•■*5, ___靈 ' :? 彌:离 A 1 lli-'*:•„•/•' .'■I .: 士 : i :: : ..... V : V . _:. : :. ::論 帽麵:圓隨__議 議___顯編顯 2 . 你的生活在爹大餐病換_!:?碓___迻些數岫你愁::; : : :乂 1 . 3. 你从哪里获得那些#为弥铪0常决策基確的信息?对宇你的重^棄膽篆息節-确度你有 多少为什么了 - / : H 复习题 (带 * 的题目涉及选读小节的内容。) 1. 正如 11.2 节说明的那样,人类会用一个问题来 表达某个目的,而不是提问。另一个例子,“你 知道你的轮胎漏气了吗”,这也是用来提醒而 不是问。给出一些问题的例子,目的是用来表 达安慰、警告或责备。 2. ‘ 把一个苏打水剂量器当作一个智能体来进行 如下 分析: 它的传感器是什么?它的效应器是 什么?它可以展现什么级别的反应(本能反 应、基于知识的反应或基于目标的反应)? 3. 确定下列每一个反应,是本能反应、基于知识 的反应还是基于目标的反应。证明你的回答。 a . —个计算机程序把文本从德文翻译成英文。 b . 当室内温度低于当前设定值时,自动调温 器打开暖气。 c . 一位飞行员驾驶飞机完全地在跑道上着陆。 4-如果一个研究人员使用计算机模型来研究人 脑的记忆能力,那么为机器所开发的程序必定 要达到机器的最佳存储能力吗?请解释。 5. 举出几个陈述性知识的例子。举出几个过程性 知识的例子。 *6. 在面向对象程序设计的环境中,一个对象的哪 些部分用来存储陈述性知识?哪些部分用来存 放过程性知识? 7. 下列活动中,你认为哪些是面向性能的?哪些 是面向模拟的? a . —个自动往返系统的设计(通常用在机场 航站楼之间)。 b . 用于预测台风路径的模型的设计。 360 第 11 章人工智能 c . 一个用于提取和维护万维网上存储的文档 目录的 Web 搜索数据库的设计。 d . 一个用于理论测试的国家经济模型的设计。 e . —个用于监视病人生命体征的程序的设计。 8. 当今,某些打给企业的电话由自动应答系统 来处理,该系统利用语音识别与打电话的人 进行通话。这些系统通过了图灵测试吗?请 解释你的答案。 9. 确定能用来区分符号 F 、 E 、 L 和 T 的一组几何 特征。 *10. 请描述通过与模板比较鉴别特性的技术与第1 章介绍的利用纠错码鉴定特性的技术之间的 相似之处。 11. 根据 T 面线绘图中标记 A 的那个角是凸起还 是凹下,说明这个图的两种解读。 A 12. 比较下列两个句子中介词短语的作用(仅有一 个词不同)。如何对一台机器编程让它做这样 的区分? The pigpen was built by the bam. The pigpen was built by the farmer. 13. 下面两个句子的语法分析的结果有什么不 同?语义分析的结果有什么不同? An awesome sunset was seen by Andrea. Andrea saw an awesome sunset. 14. 下面两个句子的语法分析的结果有什么不 同?语义分析的结果有什么不同? TfX10 then add 1 to X else subtract 1 from X. 15. 正文中,与形式程序设计语言相比,简要讨论 了理解自然语言的问题,作为讨论自然语言所 涉及的复杂性方面的例子,给出问题 “Do you know what time it is?” 有不同含义的情形。 16. —个句子上下文的改变能够改变这个句子的 含义以及意思。在图 U -3 的上下文中,如果两 人都出生于 21 世纪晚期,那么句子 “Mary hit John.” 的含义怎样改变?如果一个出生在 20 世纪 80 年代,而另一个出生在 21 世纪晚期,那 么含义如何改变? 17. 画一个语义网,把下列段落的意思表示出来。 Donna threw the ball to Jack,who hit it into center field. The center fielder tried to catch it, but it bounced off the wall instead. 18. 有时候回答一个问题的能力依赖于知识水平, 这与对事实本身的依赖程度相同。例如,假定 数据库 A 和 B 都包含一个完整的雇员名单,该 名单与公司健康保险程序相关联。但是只有数 据库 A 知道名单是完整的。那么关于一个不在 名单里的员工,数据库 A 能够推断出的什么信 息是数据库 B 推断不出来的? 19. 举出一个封闭世界假设导致矛盾的例子。 20. 举出两个共用一个封闭世界假设的例子。 21. 在产生式系统中,状态图和搜索树有什么 区别? 22. 依照一个产生式系统分析解决魔方问题的任 务。(什么是状态?什么是产生式?) 23. a . 假定搜索树是一个二叉树,达到目标需要8 个产生式。如果该树是以广度优先的方式 构建的,那么当达到目标状态时,树中最 多的节点数是多少? b - 解释通过同时构建两个搜索如何能够减少 搜索过程中考虑的全部节点数目-个 搜索从初始状态开始,同时另一个搜索从 目标状态逆向进行直到这两个搜索会合。 (假设记录在逆向搜索过程中发现的状态 的搜索树也是一个二叉树,并且两个搜索 以相同速度进行搜索。) 24. 正文中我们提到,产生式系统通常被用来作为 从已知事实中得出结论的一种技术。系统的状 态是推理过程的每一个阶段认为是真的事实, 产生式对于操纵已知事实来说是逻辑规则。标 识几个逻辑规则,支持从事实 “John is a basketball player u Basketball player are not short ’,’ 以及 “John is either short or tall ” 中能 够得出结论 “Johnis tall ”。 25. 下面的树表示一个竞赛游戏中可能的移动,选 手 X 当前可在移动 A 和移动 B 中选择其一。选 手 X 移动后,选手 Y 跟着选择移动,然后由选 手 X 来移动最后 一 步。树的叶子节点标记为 W 、 L 或 T , 分别代表选手 X 最后是贏、输还是 平局。选手 X 应选择移动 A 还是移动 B ? 为什 么?在一个竞赛性的游戏中选取一个“产生 式”和八数码游戏等单人游戏中的选取有什么 不同? 复习题 361 26. 按照产生式系统分析跳棋游戏,并描述一个用 来在两个状态中确定一个更接近目标的状态 的启发。这种情况中的控制系统与一个八数码 游戏等单人游戏中的控制系统有什么区别? 27. 把代数定律看作产生式,代数表达式简化的问 题就能在产生式系统的上下文中解决。确定一 组代数产生式,使等式 3/(2 x - l )=6/(3 x + l ) 简化 为 x =3。 当进行这种代数简化时,经验法则(即 启发法则)是什么? 28. 不用任何启发信息的帮助,画出利用广度优先 搜索方法解决如下初始状态的八数码游戏生 成的搜索树。 1 3 4 2 5 7 8 6 29. 利用图 11-10 的最佳算法解决第28题的八数码 游戏,用未到达正确位置的方块的数目作为启 发信息,画出搜索树。 30. 利用图 11-10 的最佳算法解决如下初始状态的 八数码游戏,假设使用与 11.3 节中一样的启发 信息,画出搜索树。 1 2 3 5 7 6 4 8 31. 当解决八数码游戏时,为什么用未到达正确位 置的方块的数目作为启发信息不如 11.3 节用的 那种好? 32. 执行二叉树搜索(参见 5.5 节)时决定考虑哪 一半列表的技术,和执行一个启发搜索时决定 要执行哪个分支的技术,二者有什么不同? 33. 注意,如果一个产生式系统的状态图中有一个 状态的启发值与其他状态相比极其低,并且如 果从这个状态到自身 '有 一个产生式,那么图 11-10 的算法会陷入一个循环,一遍又一遍地 考虑这个状态。说明如果执行该系统中任何产 生式的代价至少为1,那么把启发值加上沿遍 历的路径到达该状态的代价,通过这样计算预 测的代价,就可以避兔这种无限循环。 34. 在一幅大的交通图上寻找两个城市间的道路, 你会用怎样的启发。 35. 请看在寻找从 Trent 到 Wildwood 的路线时,根 据图11_10中的最佳适应算法得出的四层搜索 树。这一搜索树中的每个节点表示地图上的一 个城市。开始节点为 Trent 。 当扩展一个节点 时,仅添加与被扩展的城市直接相连的城市。 在每个节点中记录到 Wildwood 的直线距离并 将其用作启发值 d 在其处理过程中,最佳适 应算法有什么不足之处吗?如果有,应该怎样 纠正? 36. A * 算法从两个主要方面修改了最佳适应算 法。首先, A * 算法记录到每个状态的实际成 本。对于地图上的路线,实际的成本便是行进 的距离。其次,当选择一个要扩展的节点时, A * 算法选择其实际成本与启发值之和最小的 节点。根据这两个修改,请绘制问题35的搜索 树。在每一个节点中记录行进至这一节点的距 离、到达目标城市的启发值,以及它们的和。 从 Dearborn 到 Wildwood 的路线是什么呢? 37. 列出可用于产生式系统的启发所具有的两个 特性。 38. 假定有两个桶,一个容量是3升,一个容量是5 升。任何时候你都可以把水从一个桶倒入另一 个桶,把一个桶倒空,或把一个桶倒满。问题 是要将正好4升的水注入5升的那个桶。说明这 -个问题如何可以设计成一个产生式系统。 39. 假设你的任务是监督两辆卡车装货,每辆车最 多可载14吨货。货物装在不同的筐里,总重28 吨,但每一筐的重量不一样,都标在各自的筐 边上。为了在两辆车上分装这些货物,你会采 用什么样的启发式? 40. 下列哪些是元推理的例子? 362 第 11 章人工智能 a . 他还没走多长时间所以没走远。 b. 因为我经常做出错误的决定,而所作的最 后两个决定是正确的,那么我将逆转下一 个决定。 c . 我有些疲倦了,所以我可能不会清晰地 思考。 d. 我有些疲倦了,所以我想我将要打个盹。 41. 描述人类解决框架问题的能力如何帮助人类 找到丢失的项目。 42. a . 模仿学习与监督学习在何种意义上相似? b. 模仿学习与监督学习在何种意义上不同? 43. 下图表示一个用于 11.5 节讨论的联想记忆的 一个人工神经网络。如果模式中只有两个神经 元处于兴奋状态,而这两个神经元被一个神经 元分开,那么它与什么模式相关联?如果网络 初始时所有单元都处于抑制状态,会发生什么 情况? 44. 下图表示用于 11.5 节讨论的联想记忆的一个人 工神经网络。如果初始模式中至少有3个神经 元兴奋,而中央神经元处于抑制状态,那么它 与怎样的稳定布局相关联?如果初始模式中只 有两个相对的周边神经元兴奋,那么将会发生 什么情况? 45,设计一个用于联想记忆 (11. 5节所讨论的)的 人工神经网络,它由一个神经元矩形队列组 成,要移动至 I 」这样的_定模式:其中一个纵列 的神经都处于兴奋状态。 46. 调整图 11-18 所示的人工神经网络中的权值和 阈值,使其在两个输入相同(全为0或全为 1) 时输出为1,两个输入不同(一个为0另一个为 1) 时输出0。 47. 画一个与图 11-5 类似的图,表示把代数表达式 7 x +3=3 x -5 简化为 ; c =-2 的过程。 48. 扩展上题的答案,说明解题时控制系统可遵循 的其他路径。 49. 画一个与图 11-5 类似的图,表示从初始事实 “Polly is a parrot” 、 “A parrot is a bird” 以及 “All birds can fly” 中得到结论 “Polly can fly” 的推理过程。 50. 与上题中的句子不同,有些鸟不会飞,如鸵鸟 或是折了翅膀的鸟。但是,要建立一个演绎 推理系统,其中把对陈述 “All birds can fly” 的所有例外都明确列出,看来并不合理。那么, 我们人类如何确定一只鸟是能飞还是不能飞 呢? 51. 详述句子 “I read the new tax law” 在不同上下 文中的不同含义。 52. 说明怎样能够把从一个城市旅行到另一个城 市的问题设计成一个产生式系统。什么是状 态?什么是产生式? 53. 假定你要执行 A 、 B 和 C 3 个任务,它们可以以 任意次序执行(但不能同时)。说明这个问 题如何设计成一个产生式系统,并画出其状 态图。 54. 对于上一题中状态图,如果任务 C 一定要在任 务 B 之前执行,那么怎样改变状态图? 55. a . 如果 (/,)) 用来表示“若一个列表中第/个位 置的项大于第/个位置的项,则把两项交 换”, 其中评 R / 为正整数,那么下面两个序 列中哪一个能更好地完成一个长度为3的 列表的排序? (1,3)(3, 2) (1,2)(2, 3)(1, 2) b . 注意,通过这种方式表示交换序列,序列能 够加入子序列> 然后重新结合形成新的序 列。使用该方法,描述一种遗传算法,用于 开发一个排序长度为10的列表的程序。 56 -假定一组机器人的每个成员都配备有一对传 感器,每个传感器都能探测到正前方 2 m 范围 内的物体。每个机器人的形状都像一个圆的垃 圾筒,能在任何方向移动。试设计一系列实验, 社会问题 363 用来确定传感器装在哪里,使得制造出的机器 57. 你做出某种决定是基于反应模式还是基于计 人能成功地将一个篮球直线抛出。你的一系列 划模式?你的回答是否依赖于问题是关于决 实验如何与一个进化系统相比较? 定中午吃什么还是作出职业决策? 丰王会问题 下面的问题有助于你分析一些与计算领域相关的道德、社会和法律问题。回答这些问题不 是唯一的目的,还应该考虑为什么这样回答,以及你的判断是否对每个问题都标准如一。 1. 核能、遗传工程以及人工智能领域的研究者对于他们研究成果的利用方式承担多大 责任?科学家对其研究揭示的知识是否负有责任?若因此产生了意想不到的后果, 怎么办? 2. 怎样区分智能和模拟的智能?你认为二者有区别吗? 3. 假定一个计算机化的专家系统因其给出好的建议而在医疗界享有盛誉。作为一医生, 在多大程度上可以让这个系统代替他(她)为病人作出治疗决定。如果医生的治疗方案 与专家系统提出的治疗方案相对立,并且后来证实专家系统是正确的,那么那个医生是 否应该对其不当治疗负有责任? 一般说来,如果一个专家系统在某个领域内很有名,那 么在多大程度上它会束缚而不是提高人类专家的判断力? 4. 许多人认为计算机的行为只不过是人类对它进行编程的结果,所以计算机不可能有自主 意志。从而,计算机也不应对它的行为负责。人脑是计算机吗?人是否在出生的时候就 事先被编程好了?人是否被他所处的环境编程?人是否要对自己的行为负责? 5. 是否有这样一些手段,科学即使能够去做,也不应当去做?例如,如果有朝一日可以造 出感知和推理能力能与人类匹敌的机器,那么建造这样的机器是否恰当?这种机器的出 现会带来什么样的问题?今天其他一些科学领域的进展正在引发哪些问题? 6. 历史上有许多例子表明,科学家、艺术家的创作活动受其所处时代的政治、宗教及其他 社会因素的影响。这样的一些因素以何种方式影响着当今的科学成就?特别在计算机科 学领域情况如何? 7. 当今,技术的进步导致一些人的工作变得多余,许多文化至少应担负起一定的责任来帮 助对这些人进行再教育。随着技术使我们越来越多的能力变得多余,社会应当或能够做 些什么? 8. 假定你收到一张计算机处理的费用为 $0.00 的账单,你该怎么办?假定你置之不理,30 天后你又收到第二张 $0.00 的催款通知单,你该怎么办?假定你依然不理睬,而30天后你 又收到了一张 $0.00 的催款通知单,而且还有提示,若不及时付款,将诉诸法律。谁将对 1 此负责? 9. 是否有这样的情况,你会把个性与个人电脑联系在一起?计算机好像在施行报复或者固执 难缠?计算机是否曾令你抓狂?对计算机恼火和对计算机所做的结果恼火有什么不同? 计算机和你生过气吗?你与别的东西,如汽车、电视机、圆珠笔,有过类似的关系吗? 10. 根据你对上面问题的回答,人在多大程度上会把一个实体的行为与智能和意识的存在 联系起来?在多大程度上,人应当做这样的关联?对于一个智能实体来说,是否可能 用有别于其他行为的方式来展现它的智能? 11. 许多人觉得,能通过图灵测试并不意味着机器有智能。一个论点是,智能的行为本身并 不意味智能。而进化论的基础是适者生存,这就是一种基于行为的测试。是否进化论意 味着智能行为是智能的前身?机器能通过图灵测试,是否意味着它们正变得有智能? 364 第 11 章人工智能 12. 医疗手段已经取得了很大的进步,人体的许多器官现在都能用人造器官或者捐赠人的 器官来替代。可以设想,终究有一天连大脑也能换。如果这变成现实,会产生什么样 的道德问题?如果一个病人的神经细胞被人造神经细胞一点点换掉,那个病人还是同 —个人吗?那个病人会觉察到有什么不同吗?那个病人还算人吗? 13. —台汽车中的 GPS 能够提供语音提示,通知驾驶员转向及进行其他操作。当驾驶员犯错 时, GPS 会自动作出调整,并在不带任何不当情绪的情况下指导驾驶员返回正确路线。 你是否认为当驾驶员要驾车去一个未知目的地时, GPS 可以减小驾驶员的压力? GPS 又会从哪些方面给驾驶员带来压力呢? 课外阅读 Banzhaf, W., P. Nordin, R. E. Deller, and F. D. Francone. Genetic Programming: An Introduction. San Francisco, CA: Morgan Kauftnann, 1998. Lu, J. and J .Wu. Multi-Agent Robotic Systems , Boca Raton, FL: CRC Press, 2001, Luger, G. F. Artificial Intelligence: Structures and Strategies for Complex Problem Solving , 5th ed. Boston, MA: Addison-Wesley, 2005. Mitchell, M. An Introduction to Genetic Algorithms, Cambridge, MA: MIT Press, 1998. Negnevitsky, M. Artificial Intelligence: A Guide to Intelligent System, 2nd ed. Boston, MA: Addison-Wesley, 2005. Nilsson, N. Artificial Intelligence : A New Synthesis. San Francisco, CA: Morgan Kayfinann,1998. Nolfl, S. and D. Floreano, Evolutionary Robotics. Cambridge, MA: MIT Press, 2000. Rumelhart, D. E. and J. L. McClelland. Parallel Distributed Processing. Cambridge, MA: MIT Press, 1986. Russell, S. and P. Norvig. Artificial Intelligence: A Modem Approach, 3rd ed. Upper Saddle River, NJ: Prentice- Hall, 2009. Shapiro, L. G. and G. C. Stockman. Computer Vision. Englewood Cliffs, NJ: Prentice-Hall, 2001. Shieber, S. The Turing Test. Cambridge, MA: MIT Press, 2004. Weizenbaum, J. Computer Power and Human Reason. New York: W. H. Freeman, 1979. 计算理论 t 章将讨论计算机科学的理论基础。从某种意义上说,正是本章所讨论的内容为计算机 科学奠定了其真正的学科地位。尽管本质上有些抽象,但该知识体系已经有许多非常 实际的应用。具体来说,我们将讨论有关编程语言能力的内在问题,以及如何通过它来构建广 泛用于因特网通信中的公钥加密系统。 本 章内赛 12.1 函康及其## 12.2 图灵机 12.3 通用程序设计语言 12.4 一个不可计算的函数 12.5 问题的复杂性 本章要讨论的是有关计算机能做什么以及不能做什么的问题。我们将看到,一种称为图灵 机的简单机器如何被用来确定机器可解问题与机器不可解问题之间的界线。我们还将确定一个 特定的问题,就是停机问题,这个问题的解决超出了算法系统的能力,所以也就超出了当今乃 至未来计算机的能力。而且,我们会发现,即使在机器可解的问题中,仍然存在一些复杂的问 题,从任何实际的角度来看还是不可解的。最后要讨论的是,复杂性领域内的知识如何被用来 构建公钥加密系统。 12.1 函数及其计算 _ 本章的目的在于研究计算机的能力。我们要理解机器能做什么和不能做什么,以及机器要 实现其全部潜能需具备要哪些特征。这里,就从计算函数的概念开始进行讨论。 从数学意义上讲, 函数 ( fiinction ) 是一组可能的输入值和一组可能的输出值之间的映射关 系,它使每个可能的输入被赋予单一的输出。例如,将以码为度量单位转化为以米为度量单位。 如果是同样的距离,每次用码作为单位度量与用米作为单位度量的结果之间存在着对应关系。 再如排序函数,该函数对每个输入的数值表都赋予了一个输出表,而输出表的数据项与输入表 一样,但是按照升序排列。还有一个例子就是加法函数,该函数的输入是一对数值,而输出值 代表的是该对输入值之和。 对于一个给定的输入,确定其具体的输出值,这样一个过程称为函数的计算。对函数进行 计算的能力非常重要,因为正是通过对函数进行计算,问题才能得到解决。为了解决一个加法 问题,就必须计算加法 函数; 为了对表进行排序,则必须计算排序函数。因此,计算机科学的 一 个基本问题就是要找到一种技术,并用这种技术来计算用于求解问题的函数。 *12.6 公钥密码学 复习题 社会问题 诔外_读 366 第 12 章计算理论 —没有 的投资额(年复利率为「)在〃年后的金额。 但是,代数式的表达能力也有它的局限性。有些函数,它的输入/输出关系太过复杂,以致 不能用代数运算来描述。这样的例子包括三角函数,如正弦和余弦函数等。如果要计算38°的正 弦值,则可能会画出相应的三角形,测出它的边长,然后计算所求的比率,而这样的一个过程 就不能表示为对数值38的代数运算。用袖珍计算器来计算38°的正弦值也是比较费劲的。实际 上,对38°的正弦值而言,必须利用较复杂的数学方法来得到一个非常接近的近似值,并将此 作为答案。 于是可以看出,当考虑的函数越来越复杂时,我们不得不应用更为强大的方法来计算它们。 然而,不管函数的复杂性如何,我们是否总能找到一个系统来计算它们?答案是否定的。有一 个结论令人难受,那就是存在这样的一些函数,它们过于复杂以致找不到定义好的、一步一步的 过程来根据输入值确定其输出值。结果,这些函数的计算就超出了任何算法系统的能力范围, 这样的函数就称为不可计算的。而有些函数,如果可以依据它们的输入值,通过算法来确定其 输出值,就称其为可 计算的 ( computable )。 在计算机科学中,可计算函数与不可计算函数之间的区别很重要。这是因为,机器只能执行 由算法描述的任务,所以可计算函数的研究最终是对机器能力的研究。如果我们能够确定这样的 12.2 图灵机 367 能力,即允许机器计算所有可计算函数的能力,并造出具有这些能力的机器,那么就可以确信, 所建造的机器的就如我们所设想的一样强大。同样,如果发现一个问题的解决方案需要计算一个 不可计算函数,那么可以得出这样的 结论: 该问题的求解超出了机器的能力范围。 1 . 同题 习 1 _ .举 出一些函数,要求它们能完全虫表格形式表示 2 :举出 t 些函激,,要求其输出可以摇述为•括其输入的一个代数表 t 达式;、..,,: 3 _, 举出一个函数,要求不能用代数式来描述。那么你的这个函数是否仍然为可计算的? 4. 古希賭数学家用直尺和圆规画图形 。 他们借此探索出一些方法来找一条直线的中点,构建一个直角, 以及画一个等边3角形。然而,他们的众计算系统’,不能完成的“计算”是什么? -- : " " : ' ... . . : ... 12.2 图灵机 在理解机器的能力以及它的局限性的工作中,许多研究人员己经提出并研究了各种不同的 计算设备^其中之一就是图灵机,它是由阿兰•图灵于1936年提出来的,而在今天,它仍然被 用作研究算法处理能力的一种工具。 12.2.1 图灵机的原理 图灵机 (Turing machine ) 是由一个控制单元组成的,它能够通过一个读/写磁头对磁带上的 符号进行读和写(如图 12-2 所示)。磁带两端可以无限延伸,并分成一个个单元,而每个单元可 以包含符号的任意 一 个有限集合,这个集合称为机器的字母表。 图 12-2 图灵机的组成 20* 纪30年代,在技参能够提供我们现在所知道的机莽之前,阿兰•图灵就提出了图灵 秕的概备。事寒上,图:灵$想的是人; f # 笔和纸禾进行计算…图灵约乳的是^一个模型, 并 Jl 利淘这个模型 ; 来研究许算 过程” S 為娘性::' 1 在 k # 不夂:1也1 年哥德 [1 尔 66 d e i ) 发表 了著名的揭示计算系统 ; 局限性的诊文,并且其研究的主要精力集中在理解这些局限性上。在 囷灵提出他的模型的同一年 ( 1936 年), 埃米尔 • 波斯特 (Emil Post ) 提出了另外一种模型 (现在 将荇称为波辦特产生式系统:}, 他 所提出的这个模型与 a ■灵的構翠有着同样的雜力 ,作 4这些早七研究人责::洞黎力的兔缸,他们的计算系统模型(如图灵机和波斯特产生式涘统等) 在计算机科学研究领域,仍然可以作为有价值的工具来使用。 在图灵机计算的任何一时刻,机器一定处在有限个条件中的一个,这些条件称为状态。图 灵机的计算开始于一个特定的状态,称为初始状态,而停止于另一特定的状态,称为停止状态。 图灵机的计算由机器的控制单元执行的一系列步骤所组成。每一步都包括观察当前磁带单 元中的符号(由读/写磁头所看到的那个),然后将符号写进这个单元,期间可能要将读/写磁头 左移或右移一个单元,接下来再改变状态。要执行的确切操作是由程序决定的,程序通过机器 的状态和磁带当前单元的内容来告诉控制单元做什么。 现在来考虑图灵机的一个具体的例子。为此,将机器的磁带表示成一条水平条带,并将条 带分成一个个单元,且单元中可以记录机器字母表里的符号。可以通过在磁带当前单元放置一 个标签来标示机器的读/写磁头的当前位置。本例中的字母包括有0、1和*。机器的磁带的样子 如图 12-3 所示。 当前位置 图 12-3 机器的磁带的样子 磁带上的符号串可以解释为由星号分开的二进制数,那么可以看出,这个磁带包含的是值5。 我们所设计的图灵机要把磁带上的这样一个值加1。更准确地说,假设开始位置是标在一串0和1 右端的星号,接下来要做的是改变其左边的位模式,使其可以表示下一个较大的整数。 我们机器的状 态有 : START (开始 )、 ADD (相加 )、 CARRY (进位 )、 OVERFLOW (溢出)、 RETURN (返回)以及 HALT (停 止)。 这些状态对应的每一个操作和当前单元的内容如图〗 2-4 中 的表所示。这里假设机器一直是从 STRAT 状态开始的。 当前 当前单 元内容 写的值 赛动方向 进久的新_ II 1 , :通襲_ ,_ I " 丨 " 〕厂 w: H ^ 'I • , ii -: 1 I i ' 1,1 '' 11 ' 1,11 ^ | Wi^!&'OV'S ;hH V ', 1 li : i' i'lT I 1 n 網獅禱 K ..• -y., : .i : ;-"V ',>L! ,";':.-i -:• I'A." •: .' 1 '| ' 'l-ll !.'! 1 'll ..... . 邀 1 . mwmi^ liK ! __讎_ ___8^繫: JS . , ... , : ■•‘〆,[-、 : :' r - ====i= 图 12~4 实现增加值操作的图灵机 现在把这个机器应用到图 12-3 所示的包含值5的磁带上。可以观察到,当处在 START 状态时, 当前单元包含 * (在本例中),图 12-3 中的表指示我们要重写*,并将读/写磁头左移一个单元, 这时就进入了 add 状态。做完这些后,机器的情况就如图 12-5 所示。 机器状态=^00 当前位置 图 12-5 机器状态为 ADD 时对应的情况 为了继续,查表看当处于 ADD 状态并且当前单元包含1时,机器要做些什么。图 12-3 所示的 表告诉我们要用0代替当前单元的1,并把读/写磁头左移一个单元,这时就进入了 CARRY 状态。 12.2 图灵机 369 这样一来,机器的情况就如图 12-6 所示。 /I 0 [ 机器状态=0义冊¥当前位置 图1 2-6 机器状态为 CARRY 时对应的情况 接下来,我们再去查表,看看当机器处在 CAKRY 状态并且当前单元包含0时要做什么。表告 诉我们应该用1来代替0,并把读/写磁头右移一个单元,这时就进入了 RETURN 状态。做完这些 后,机器的情况就如图 12-7 所示。 1 1 K 1 ' : v i-' i:-i h v ii (i'i o ; !:/ . + ^1- ■■ HE. 机器状态 ^RETURN 当前位置 图 12-7 机器状态为 RETO 厕时对应的情况 根据这个情况,表指示我们用另一个0来代替当前单元中的0,并把读/写磁头右移一个单元, 这时就保持在 RETURN 状态。结果,机器的情况就如图 12-8 所示。 机器状当前位置 图 12-8 保持 RETURN 状态时对应的情况 在这个时候,可以看到,表指示我们在当前单元中重写*,同时进入 HALT 状态。于是,机 器就停止在如图 12-9 所示的情况(磁带上的符号就表示了所需要的值6)。 餐 1 1 0 .... 机器状态=1^1^ 当前位置 图 12-9 机器状态为 HALT 时对应的情况 12.2.2 丘奇-图灵论题 前面例子中的图灵机可以用来计算所谓的后继函数,使用这种函数,每个非负整数输入值《 的输出 值为〃 +1。我们只需要把用二进制形式表示的输入值放在机器的磁带上,运行机器,直至 停止,然后就可以从磁带上读取输出值。由图灵机以这种方式计算的函数称为图灵可计算的 (Turing computable ) 函数。 图灵猜想 是指: 图灵可计算函数与可计算函数是一样的。换句话说,图灵猜想,图灵机的 计算能力囊括了任何算法系统的能力,或者同样也可以这么说,(与表格和代数公式这些方法形 成对比)图灵机概念提供了一个环境,在此环境下,所有可计算函数的解都能够被表示。在今 天,这个猜想通常被称为丘奇-图灵论题 ( Church-Turing thesis ), 这是为了纪念阿兰•图灵和阿 隆佐•丘奇这两个人的贡献。自从图灵的最初工作以来,已经收集了许多支持这个论题的例证, 370 第 12 章计算理论 现在,丘奇-图灵论题已经被广泛接受了。也就是说,可计算函数与图灵可计算函数被认为是一 回事。 这个猜想的意义就在于,它领悟到了计算机器的能力和局限性。更为准确地说,它把图灵 机的能力确立为一种标准,因而其他计算系统就能够与此进行比较。如果一个计算系统能够计 算所有的图灵可计算函数,那么就可以认为它的能力与任何计算系统的能力相当。 : •' V ... :0 •.,••:: • ' ! . : ,: : . . . ; ;":: : : ! -- : • • I : : , •; V ;' ;': : : :- ■ - : 1 : - - - ■ - : 1 1. 应用本节所籀述的图灵机(见图 124) ,从如下的初始状态开始。 /I 斧 • _: 1 1 0 , . 1 / . • ... • ... . . . 1 . : :' : • ' 1 , _ • il ' 机器状当前位置 2, 描述一个图灵机,要求用一个0来替换一串0和 1。 丄韻述^个图灵桃,某襄求是 r , 如果磁辩上的值大»:,则该值要减1;如果磁带上的值为%则该值保持 - 11 :' ::'V 4. 请举出一个日常生活的场景,要求该场景中要有计算的活动发生。这个场景每样与图灵机进行类比? 5. 描述一个图灵机,要求它在输入某 些值时 最终会停机,而在输入其他值时永不会停机。 12.3 通用程序设计语言 在第6章中,我们讨论了高级程序设计语言中的各种特性。本节中,我们要应用可计算性方 面的知识来确定这些特性中哪些特性是真正必需的。我们会发现,当今的高级语言中的许多特 性仅仅是增强使用的方便性,而对语言的基本功能并没有什么贡献。 我们的方法是描述一种简单的指令性程序设计语言,而这种丰富的语言足以用来表达计算 所有图灵可计算函数(因此也包括所有可计算函数)的程序。因此,如果以后的程序员发现一 个用这种语言解决不了的问题,那么其原因并不在于这种语言的缺陷。相反,问题就出在没有 解决这个问题的算法。具有这种特性的程序设计语言称 为通用程序设计语言 (universal programming language ) 。 你也许会惊奇地发现,一种通用程序设计语言其实并不需要很复杂。事实上,我们将要介 绍的这种语言将会非常简单。因为它是从通用程序设计语言中分离出来的需求的最小集合,所 以将它称为 Bare Bones (基本要素)语言。 12.3.1 Bare Bones 语言 为了表述 Bare Bones 语言,这里就先来考虑其他程序设计语言中的声明语句。尽管机器本 身只能处理二进制位模式,并且不知道模式所表示的内容,但是利用这些声明语句程序员可以 从数据结构和数据类型(如数值数组和字符串等)方面考虑问题。用来处理精巧的数据类型和 数据结构的高级指令在提交给机器执行之前,必须被翻译成机器指令,这些指令操纵位模式来 模拟所需的操作。 为了方便,可以将这些位模式解释成二进制符号表示的数值。这样一来,由计算机完成的 所有计算都能够表示成包括非负整数的数值计算,这是有目共睹的。而且,如果要求程序员按 这种方式表示算法,那么程序设计语言就能得到简化(尽管这会大大增加程序员的负担)。 12.3 通用程序设计语言 371 由于我们开发 Bare Bones 语言的目标是开发出最简单的语言,所以我们将遵循这个思路。 Bare Bones 语言中的所有变量都考虑表示成位模式,为了方便,我们将其解释为二进制符号表 示的非负整数。这样一来,一个当前被赋值为模式10的变量将包含值2,而被赋值为模式101的 变量将包含值5。 利用这种约定 , Bare Bones 程序中的所有变量都属于同一种类型,这样一来,这种语言就 不需要声明语句来描述不同变量的名字和与之相关的属性。当利用 Bare Bones 语言时,程序员 可以在需要时只使用一个新变量名即可,这里,程序员理解的是,它是一个解释成非负整数的 二 进制位模式。 当然,用在 Bare Bones 语言中的翻译器必须能够把变量名和其他术语区分开来。要做到这 一点,需要设计出 Bare Bones 语言的语法,以便只需通过语法就可以识别出任何术语的作用。 为了达到这个目的,我们 规定: 变量名必须以英文字母开始,后面可以跟字母和数字 (0-9) 的任意组合。这样一来,字符串 XYZ 、 B 747 、 abcdefghi 以及 X 5 Y 都能用作变量名,而 2 G 5 、%o 和 x . y 就不能。 现在,让我们来考虑 Bare Bones 语言中的过程语句。这里有三个赋值语句和一个表示循环 的控制结构语句。该语言是一种自由格式的语言,于是每条语句都以分号结束,这使得翻译器 很容易将出现在同一行里的语句分割开。然而,为了增强可读性,这里仍采用的是每行只写一 条语句的原则。 三条赋值语句,每条都要求改变语句中所标识的变量的内容。第一条语句可以让一个变量 清零,其语法为 clear name-, 其中 name 可以是任何变量名。 另外两条赋值语句的作用本质上是相 反的: incr na.me ; 和 deer name; 同样, name 表示任何变量名。第一条语句使标识的变量值增加1。这样一来,如果变量 Y 原来的 值为5,那么执行语句 incr Y ; 后,赋给变量 Y 的值就变为6。 相反, deer 语句被用来将标识的变量值减1。一种例外的情况是,当变量的值已经为0时, 这条语句将保持值不变。所以,如果变量 Y 的值为5,那么执行语句 deer Y ; 后,变量 Y 所赋的值就为4。然而,如果变量 Y 的值已经为0,那么执行这条语句后,该变量的值 仍为0。 Bare Bones 语言只提供了一条控制结构语句,该语句由 while-end 语句对表示。语句序列 while name not 0 do; (其中 Wname 表示任意变量名)在变量 name 不为0的情况下,使位于 while 与 end 之间的任何语 句或语句序列反复执行。更为准确地说,在程序执行期间遇到 while-end 结构语句时,所标识 变量的值首先和0进行 比较: 如果值为0,则跳过此结构,继续执行 end 后面的 语句; 然而,如 果变量的值不为0,那么就执行 while-end 结构中的语句序列,并且控制回到 while 语句,于是 再进行比较。注意,程序员要担起循环控制的一部分责任,为了避免陷入无限循环,程序员必 须在循环体中明确要求改变变量的值。例如,语句序列 incr X ; while X not 0 do; incr Z ; end; 将会导致一个无穷的循环过程,这是因为一旦到达 while 语句, X 的值永远不会为0。而语句序列 clear Z ; while X not 0 do; incr Z deer X ; end ; 最终会停止。该语句的作用是将 X 的初始值转移给变量 Z 。 可以观察出, while 和 end 语句必须成对出现,且 while 语句在前。然而, while-end 语句 对也可以出现在被另一个 while-end 语句对重复执行的结构中。在这种情况中, while 和 end 语句配对是这样实现的,即先按程序的编写形式从头到尾地对程序进行扫描,并将每条 end 语 句与其最近的、还没有配对的前面一条 while 语句关联成一对。虽然在语法上并非必需的,我 们通常还是采用缩进的形式来增加这种结构的可读性。 最后一个例子是图 12-10 中的指令序列,该序列执行的结果是将 X 和 Y 的值的乘积赋给 Z , 虽然 有一个的副作用,即会破坏已经赋值给 X 的任何非零值。 ( 由变量 W 控制的 while-end 结构起到了 恢复 Y 的初始值的作用。) 图 12-10 —个用于计算 xx y 的 Bare Bones 程序 12.3.2 用 Bare Bones 语言编程 记住,我们提出 Bare Bones 语言的目的就是要研究什么是可能的,什么是不切实际的。如 果要在实用的场合使用 Bare Bones 语言,事实证明将不太合适。另一方面,我们将很快看到, 这种简单的语言达到了我们的目的,即提供了一种基本的通用程序设计语言。在这里,我们只 是要说明一下如何用 Bare Bones 语言来表示一些基本的操作。 12.3 通用程序设计语言 373 首先可以注意到,运用几个赋值语句的组合可以把任何值(任何非负整数)赋给一个指定 的变量。例如,以下语句序列用来实现把值 3 赋给变量 X ,即先将值 0 赋给 X ,然后对其值进行三 次递增 操作: clear X ; incr X ; incr X ; incr X ; 程序中另一种常见的活动就是将数据从一个地方复制到另外一个地方。就 Bare Bones 语言 而言,这就意味着我们需要能够将一个变量的值赋给另外一个变量。可以这样来 实现: 先将目 标变量清0,然后对其进行合适次数的递增操作。事实上,我们已经看到,语句序列 clear Z ; while X not 0 do ; incr Z ; deer X; end ; 把 X 的值转移到了 Z 。 然而,这个语句序列还有一个副作用,即破坏了 X 的初始值。为了避免这 一 现象,可以引入一个辅助变量,先将对象的值从其初始位置转移至这个辅助变量。于是,就 可以将这个辅助变量作为数据源,并从中恢复初始的变量,同时将变量的值放至所要求的目标 位置上。通过这种方式,图 12-11 所示的语句序列实现了 Today 到 Yesterday 的转移。 clear Aux ; clear Tomorrow ; while Today not 0 do ; incr Aux; deer Today ,- end; while Aux not 0 do ; incr Today ; incr Tomorrow; deer Aux ; end; 图 12-1 1 实现指令“ copy today to tomorrow” 的 Bare Bones 语句序列 我们采用语法 copy namel to name2 ; - ( 这里 na / nel 和 name 2 都表示变量名)作为一种简略的符号,用来表示图 12-5 所示的语句结构。 这样一来,尽管 Bare Bones 语言本身没有明确的 copy 指令,但是在写程序的时候就好像有这样 的指令。而这里需要理解的是,要将这种非正式的程序转化成实际的 Bare Bones 语言程序,必 须把 copy 语句用其等价的 while-end 结构来代替,并且所使用的辅助变量名不要与程序中其他 地方已经用过的名字相冲突。 12.3.3 Bare Bones 的通用性 现在就应用丘奇-图灵论题来证明我们的论断,即 Bare Bones 语言是一种通用程序设计语言。 首先,可以看到,任何用 Bare Bones 语言所写的程序都能看作是对一个函数计算的指导。函数的 输入包含的是程序执行前赋予变量的值,并且函数的输出包含的是程序结束时变量的值。为了计 算这个函数,只要从变量的适当赋值开始执行这个程序,然后再观察程序终止时变量的值。 374 第 12 章计算理论 在这些条件下,程序 incr X; 负责计算由 12.2 节中图灵机例子所计算的同一个函数(后继函数)。事实上,它将 x 的值增加1。 同样,如果将变量 X 和 Y 解释成输入,而将变量 z 作为输出,那么程序 copy Y to Z ; while X not 0 do ; incr Z ; deer X; end; 负责加法函数的计算。 研究者己经证明 , Bare Bones 程序设计语言能够用来表示计算所有图灵可计算函数的算法。 如果把这与丘奇-图灵论题相结合,就意味着任何可计算函数都能由 Bare Bones 语言编写的程序 来进行计算。这样一来 , Bare Bones 语言就是一种通用程序设计语言。从这个意义上讲,如果 存在一个解决问题的算法,那么通过一些 Bare Bones 语言程序就能解决这个问题。因此,理论 上可以这 么说 : Bare Bones 语言可以用来作为一种通用程序设计语言。 之所以是 从理论上讲, 是因为这样一种语言当然不像第6章中介绍的高级语言那样方便。 但是,每种高级语言实质上都包含有 Bare Bones 语言的特性,并将其作为核心。实际上,正是 这个核心,才保证了每种这样的语言的通用性,而各种语言中的其他特性都是为了方便使用 才包括的。 尽管像 Bare Bones 之类的语言在应用程序设计环境中并不实用,但在计算机科学的理论研 究中还是能找到用武之地的。例如,在附录 E 中,将使用 Bare Bones 语言作为一种工具来解决第 5章所提出关于迭代结构和递归结构等价的问题。事实上我们会发现,这种等价性的猜测证明是 正确的。 ______: ^'^,,11: : ' ;:'- r ■: :: ;|.;:.:' :::':• : ,'::•:::•••..•:;• ! ly . : ! , ;•. : 1 •. •::_:: : ..... : 1 :. !. " : ..... ... lv 证明藉句 invert X_; (此语句的功能是如果袁的初始值非那么1把 X 的值转化为0;如果初始值 -为0, 么就将该值转化为1 ) 能夢用一段 Bare ffemes 程序段来进行模拟。 2. 证明即俾筒单的 Bcmes 语言也包含了一些非必要的语句,如 clear 语句能利用辑言中剔的语句的 |__|||^_議.:^ - : ,■: : u : ; ;;: : : r : ' . 5 ]:| r : ' : 3 二证明 if-the _ n-else 结构能够由 Bare Bones 语言乗模拟。也就是说,用 Bare Bon6s 语言写一段程序序 4列,用来模拟以下语句的 操作: ' _: . . . — ■■ - L ... : ' -- * , if % not 0 then SI else S2; :__濯__隱園_ ■權鐘 ■議_漏3擊 ;■;!,■■ ;■:];' : 4,证明:每一条 Bare B 加 es 语句都_用附录 C 的机器语言来表达。(所以 Bare? ©ones 语言可以作为这样一 种机襻的编程语言。),: 5- - 怎样用 Bare Sones 语言来处琿负 :数? 6- 摘泽: ©^^BareJBcm 印雍 :)# 计算的興数 y: 锒设该函数的输乂由 xi 示 ,输出,由 €表示。 1 11 : ^ : clear while Z not 0 do 12.4 一个不可计算的函数 375 deer X ; . . 二 :。-..-: ' ' : , 二; ■- -- s i _ — — _ • : .yy •- n ” •:. .•-: .v._. end ; '■ : - : . . -L; ■、:卜, w 1 ■■■■ ^ - ■…守 ,:: "(Wirumrj 足热 d 1 - t+ 货料你 f 抑:- nwv 躺以怒:::. /'v¥p. ;fe-.、. .-和 -秦— )/v)awr_- 'r:l: V V--? H ' 0、 • Jr— * '- u - I- - _ —'J*. w ■ST ../';s : :■ ■■ 」 ■ , s 码为式 on 码模 sc 编位 A 序长 用程.个 利将 I v」(;:,-' vi;:v ; u.o;;;.': ; . r , v ',#-. E .''《 w ■ l . ■ -1-- - , ,浐 =:...-'x s_:x )々 ;.; h_ r:rfe&.:;-_:::v :: e 12.4 一个不可计算的函数 377 输出值1,而表示一个非自终止程序的输入则产生输出值0。为了简明起见,称这个函数为停机 函数 ( halting function ) 0 我们的任务就是要 证明: 停机函数是不可计算的。所用到的方法是“反证法”。简而言之, 要证明一条语句为假,只需证明它不为真即可。于是,证明语句“停机函数是可计算的”不为 真。我们的整个的论据都概括在图 12-13 中。 ::驗 ,賴奄 这样翻 i 華, ㈤ —•:: ■■乂 : 说紘:剩_式■衰 ::: 一 .… r L , ->h r 十 II I - I - I 1 1 I \ ' -H 1 J H L - ■ , l '''-^' 13 * Tl ^ 說繁提议的程序 ■ 提议的程序 j 雜 g I ' T I - while x not 0 do; end ; 11^ , r - -:s - : ^ u 。 L -'. 卜 -. v . . 二丄 .1 ....., : ;!;:!:::.! ;-|^ : :Kr!:V;:S^:.-.;^l!::!::^!lv-%v:^;':!'v:'.:|.^:.!'i;-! : ''i''''' : ' .'' ............. :..童 f 葬 n M' 1 1 丨 V V ; ‘, -,,_J I i : ' : ' ; :V : 提议的程序 r J I v I r rlL r L T M !■- h'n HIM . nH Ml 3 ' Hhl ' H .:斤驟_驗韻敏:,、 ;頌4鱗_雞:績 :喆:麵醎嘯 ,穴 〆 S :_: 體:二 i H I " r V > _ - x ' x ' *'-- L:r :_:i 觀 L 1 ■- UI ' L - I I - 1 I n I -I J 1- 1- ' H ^ H | l - I " Z.I J J \ _: v :: 二 ^ 伽 謂球 r , r〆 : :;: 'f./j'vi 提议的程序 L. U _while x J not 0 dG; : ;b| end ; :;,^ 1 :■, 』-: i [ M I- -I hr Si . Mi-^ Vs ^ 严 I 11 r T 1 - _:Y 囊 : _ 娜 _ 嘴 :: ' _「■ 二: :, - 檠編 ::: :/〜 _ 111 ;:d while :: i .v : ? : : 1 : i^ 1 取平产 v /。 ^ M h i'T 4 [ _ k L :二 U K:yCn 1 '丨 提议的程序 .!。|.+.、;.-;. 4 .;| y ,卜 I J 嫩 . - M 提议的程序 Li i V'^."_ :.〉 詹麵:七: 5 'Iwhile : U _._!齡 end■ 卜二 說 3: 绿或 微總兹 :靖 I V | V'i ij 卜 I 。 ■ r s _ t , ,- — 4:鞭焉嫌 I ;:::.' 卜赠條獻.1. : :; , not ° do ; 卜■'雜 f » it 猶:,:: p …: 1 ' CU ' +.4 rv ■'乂 '^ nrd; 」 」cv r ,, A :;::':劳〜旧心:藝«»::/ : - ; " : .“...!1. ,1 I |..' .•: r y t J 4 t-' -X- ;■;.; 卜 -rT:l;-r;Vi-:H KH .-,:-: - '•.''•; j; Jj; n..! ,■ %''釋 ::㈣ 带濟纖讀濟輸 ㈤ , 及嫌 1_筹_娜雜 图 12-13 证明停机程序的不可解 如果停机函数是可计算的,那么(因为 Bare Bones 语言是一种通用程序设计语言)一定存 在一个能计算该函数的 Bare Bones 程序。换句话说,存在一个 Bare Bones 程序,如果它的输入是 一 个自终止程序的编码版本,那么它就将以输出值等于1而终止;否则就以输出值等于0而终止。 为了用这个程序,我们并不需要确认哪个变量是输入变量,而只需把程序的所有变量初始 化为被测试程序的编码表示即可。这是因为,如果一个变量不是输入变量,那么它的初始值本 质上是不会影响到最终的输出值的。所以,可以这么说,如果停机函数是可计算的,那么就存 在下面这样一个 Bare Bones 程序: 如果其所有变量都初始化成一个自终止程序的编码版本,那 么它将以输出值等于1而 终止; 否则将以输出值等于0终止。 378 第 12 章计算理论 假设该程序的输出变量名为 X (如果不是,则对变量进行简单的更名即可),我们可以在程 序的最后加上以下的语句来修改这个程序,从而产生一个新 程序: while X not 0 do ; end ; 而这个新程序必须要么是自终止的,要么就不是。然而,我们将会看到,它既不是自终止的, 也不是非自终止的。 具体来说,如果这个新程序是自终止的,且用初始化为该程序自身的编码表示的变量来 执行这个程序,那么当它执行到我们所加的 while 语句时,变量 X 将为1。(在这一点上,这个 新程序与原始程序一样,如果其输入是一个自终止程序的表示,那么就会产生一个1。)在这 一点,程序的执行将会始终陷入在 while - end 结构中,因为在这个循环中没有提供让 X 值递减 的措施。但是,这与关于新程序是自终止的假设相矛盾,因此,我们必然得出 结论: 新程序 不是自终止的。 然而,如果这个新程序不是自终止的,且用初始化为该程序自身的编码表示的变量来执行 这个程序,那么当它执行到我们所加的 while 语句时,变量 X 就被赋值为0。(之所以会这样,是 因为在该 while 语句之前的语句构成的原始程序,在其输入表示一个非自终止程序时,输出0。) 在这种情况下,不会执行 while _ end 结构中的循环,程序会停止。但是,这正是自终止程序的 特性,所以,我们不得不得出 结论: 该新程序是自终止的。这正如早些时候我们不得不认为它 不是自终止的。 概括来说,可以看出,我们遇到了程序中的一个不可能的情况,即一方面程序必须要么是 自终止的,要么 不是; 而另一方面程序又必须既不是自终止的,又不是非自终止的。其结果是, 导致这种矛盾的假设必定不成立。 我们可以得出 结论: 停机函数是不可计算的。因为停机问题的解决依赖于这个函数的计算, 所以必然得出 结论: 停机问题的解决超出了任何算法系统的能力范围。这种问题被称为不可解 问题 ( 皿 solvable problem ) 。 最后,把刚才讨论过的内容与第10章中的思想联系起来。第10章中一个非常基本的问题就 是计算机器的能力是否包含智能本身所需要的能力。回想一下,机器只能解决使用算法可解的 问题,而现在已经发现有些问题使用算法无法解决。因此,问题就在于人类的大脑是否包含了 比执行算法过程更多的东西?如果没有,那么我们在这里所确定出的局限性,也就是人类思想 的局限性。不必说,这是一个极具争议的问题,有时也是情绪方面的问题。例如,如果人的大 脑只不过是编程过的机器的话,那么可以推断出,人类就不再拥有自由的意志。 1. 下面细 B 取 e Bones 辱序舉 g 續 解鲁_0箐寒:。 . , "xncr- :七屮 ;::■::»::;.; 钴鉍恶 」, y . 龙 $ 汍沒恕 g 这综茂 泛:鬯 : : 2. 卞面的 B — Spue 適序是自终止的吗?请 解 释你的答案。 __ , : f S_S|_S_@3W ■ 囉 ^ 擊 ' ^ Y y x- . : . v _ " - , ; ;: Li^^ :" i:: ;: ■iC:',-?H v i3 : in.M'S : !::'3V:-: ’ ■: ■" :■- while X not 0 dp ; 12.5 问题的复杂性 379 deer X; "deer Y ; ' ^decr Y ? end; deer .Y ; while Y not^ end ; ' l do. 在某 个社 a 里 — 毒个人都钾有 自:己的房于 .社匿的房泼 油:泰本声: .,:::“樣 奏一:蘇:揉内餐 ^所有房崖进行涂漆?位是只针对那些没!有被雇主自:己涂过 的養屋 ^ i : k 0 i 蠢来無部漆 —|, . ::: :1"; I , ••:•.'■■;' ••;•• : •' •?', .0;- •;! ';;!,;! :: : .1. " ..I - : •! -■ :' •••:' !'■•''• . . V .. 丫丫 : . I 乂:::: I .: :.." 1 : 1 "‘:. 乂 •. -.卜 _ 1.1 V . :• . vmumsmwi 12.5 问题的复杂性 在 12.4 节中,已经讨论过问题的可解性。本节我们关注的问题是一个可解的问题是否有一 个实际解。我们将会发现,有些问题在理论上是可解的,但由于过于复杂,从实际的观点来看, 它们是无解的。 12.5.1 问题复杂性的度量 我们首先回到 5.6 节中的关于算法效率的研究。在那里,我们用大写的希腊字母 © 来标记, 并根据算法执行所需的时间来对其进行分类。我们发现,插入排序算法属于 0( n 2 ) 这一类,顺 序查找算法属于0»,而二分查找算法则属于 ©( lg «)。 现在,利用这个分类系统来帮助我们 确定问题的复杂性。我们的目标是开发出一种分类系统,使其能告#我们哪些问题比另一些问 题更为复杂,并最终确定出哪些问题太过复杂,以致实际上不能解。 我们现在的研究之所以是基于算法效率的知识,其原因在于希望从解题的复杂性角度来衡 量一个问题的复杂性。我们 认为: 简单问题有简单的 解法; 复杂的问题就没有简单的解法。可 以注意到这样一个事实,一个问题有一个复杂的解并不一定意味着该问题本身很复杂。毕竟, 一个问题可以有许多解,而其中的某个必然会复杂些。所以,如果要确定一个问题本身很复 杂,那么就需要证明它的所有解都不简单。 在计算机科学领域中,让人感兴趣的问题就是那些机器能够解的问题。这些问题的解都明 确地表示为算法。所以,问题的复杂性取决于解决该问题的算法的特性。更为准确地说,解决 一个问题的最简单算法的复杂性可以被认为是该问题本身的复杂性。 但是,如何来衡量一个算法的复杂性呢?遗憾的是,术语复杂性 ( complexity ) 有着不同的 解释。一种解释就涉及一个算法中所包含的判定和分支数量。如果按照这种理解,那么一个复 杂的算法将会有着盘根错节的判定和分支。这种解释也许能够和软件工程师的观点相一致,软 件工程师对与算法发现和表示相关的问题感兴趣,但是这并没有获得从机器的观点所看到的复 杂性的概念。机器在选择下一条要执行的指令时,其实并没有做实际的判断工作,它只是一遍 一 遍地遵循机器周期,每次执行的都是程序计数器所给出的指令。所以,机器能够执行一组看 上去很杂乱的指令,而事实上,它就像在执行一串简单有序的指令那样轻松。所以,复杂性的 解释倾向于度量一个算法在表示中所遇到的难度,而不是算法本身的复杂性。 380 第 12 章计算理论 从机器的角度来看,能够更为准确地反映算法复杂性的一种解释是,要度量执行这个算法 时所必须完成的步骤数。注意,这个数目与写好的程序中所出现的指令数目是不一样的。其循 环体只有一条语句,但是其控制要求这个循环体执行100次的循环,在它被执行时,就相当于执 行 100 条指令。所以,这样一个例程被认为要比一串 50 条分开写的语句更为复杂,尽管后者在书 写形式上显得更长。复杂性最终与机器在执行一个解法时所花的时间,而不是与用来表示解的 程序的大小相关。 所以,如果一个问题的所有解都需要大量的时间,那么就认为这个问题是复杂的。这种复 杂性的定义称为时间 复杂性 (time complexity ) o 在 5. 6节中介绍算法效率时,我们已经间接地遇 到了时间复杂性这个概念。毕竟,对算法效率的研究就是对算法时间复杂性的研究,只是两者 是对立的。也就是说,“较高的效率”等于“较低的复杂性”。所以,从时间复杂性的角度看, 在解决一个表单搜索问题时,顺序查找算法(属于 ©(«)) 要比二分查找算法(属于 ©( lgn )) 更为复杂。 现在,我们运用算法复杂性方面的知识来获得一个确定问题复杂性的方法。在解一个问 题时,如果存在一个算法,其时间复杂性为 0( A «)), 并且解决该问题的其他算法没有比这更 低的时间复杂性,那么我们就定义这个问题的(时间)复杂度为©(«)),这里 /(«) 是 n 的某 个数学表达式。也就是说,一个问题的(时间)复杂性定义为该问题的最优解的(时间)复 杂性。但是,找到一个问题的最优解和确认该解为最优解本身往往就是一个难题。在这样的 情况下,大 0 标记的变种,称为大0 标记 (big O notation ), 被用来表示对一个问题的复杂性 的了解程序。更为准确地说,如果/(/0是《的某个数学表达式,并且如果一个问题能够被属于 0( A «)) 这一类的算法所解决,那么我们就说,这个问题是属于 0( A «)) 这一类的。这样一来, 如果说一个问题是属于 O 的,那么也就意味着这个问题有一个复杂性属于 ©( AW ) 的解, 但是它可能还有更优解。 对查找和排序算法的研究告诉我们,一个长度为《的表(我们只知道表预先已经排序好)的 查找问题是属于 0( lg «) 的,这是因为二分查找算法能够解决这个问题。而且,研究人员已经证 明,查找问题确实是属于 ©( lg «) 的,所以二分查找算法就代表了这个问题的最优解。相反,我 们知道,对一个长度为《的表(这时是不知道表中原始值的分布情况)的排序问题就属于0(« 2 ;), 这是因为是用插入排序算法来解决这个问题的。然而,知道排序问题是属于这就告 诉我们,插入排序算法不是最优解(从时间复杂性的角度看)。 排序问题的一个更好的解决办法是归并排序算法。其方法是将表的一些较小的、排序过的 部分归并成较大的、排序好的部分,然后再进行归并,得到更大的排序过的部分。每次归并过 程都是利用在介绍顺序文件时所遇到的归并算法(如图 9-15 所示)。为了方便,再用图 12-14 来 表示,而这次的情况是归并两个表。完整的(递归)归并排序算法可由图 12-15 中所示的 MergeSort 过程来表示。当要求对一个表排序时,这个过程首先检查被排序的表,看其是否少于两个数据 项: 如果是,则该过程的任务已经 完成; 如果不是,则这个过程将表分成两部分,再请求过程 MergeSort 的另外一个副本对这两部分进行排序,然后将这些排序好的片段合并在一起,这样 就得到最后的排序过的表。 为了分析这个算法的复杂性,首先来考虑在合并一个长度为 r 的表和一个长度为 s 的表时,必 须要在表的数据项之间进行比较的次数。归并过程是这样进 行的: 重复地对一个表中的数据项与 另一个表中的数据项进行比较,然后将两者中的“较小项”放入到输出表中。这样一来,每做一 次比较,那么还要考虑的(也就是未比较的)数据项的数目就要减1。由于开始时只有 r + s 个数据 项,那么我们就可以得出 结论: 这两个表的归并过程所包含的比较次数不会多于 r + s 次。 12.5 问题的复杂性 381 对表的第 对表的第 对表的第 对表的最 .二个>序三个士_ 4排序 图 12-16 由归并排序算法产生的问题的层次结构 首先,我们来确定树的每层上所进行比较的次数。可以看出,出现在树的任意一层的每个 节点,其任务都是对初始表的一个特定段进行排序。这个工作由归并过程来完成,因此正如我 们已经指出过的,所要求的比较次数不会多于该表段中的数据项的数。因而,树的每一层所需 的比较次数不会多于该表段中的数据项的总数,而且,因为树中所给定的一个层的段表示的是 初始表所分割的部分,因而这个总数不会比初始表的长度大。因此,树的每一层所包含的比较 procedure MergeLists ( InputListA , InputListB , OutputList ) if (两个输入表 为空 ) then ( Stop , OutputList 为空) if ( InputListA 为空) then (声明它是饥饿的) else (声明它的第一项为当前项) jf(lnputListB 为空) then (声明它是饥饿的) else (声明它的第一项为当前项) while (两个输入表都不是饥饿的 ) do (把较小的当前项放入 OutputList : if (该当前项是对应输入表的最后一项) then (声明该输入表是饥饿的) else (声明该输入表的下一项为该表的当前项) ) 从未完的输入表中的当前项开始,剩下的项复制到 OutputList 。 图 12-14 用来合并两个表的过程 MergeLists procedure MergeSort ( List ) if ( List 有多项) then (应用过程 MergeSort 来对 List 的前半部分进行排序; 应用过程 MergeSort 来对 List 的后半部分进行排序; 应用过程 MergeLists 合并 List 的前半部分和后半部分生成一个已排序的 List 图 12-15 实现为过程 MergeSort 的归并排序算法 现在来考虑完整的归并排序算法。它是通过这样的方式来处理对一个长度为《的表的排序工 作,即将最初的排序问题简化为两个相对较小的问题,每个问题是要对一个长度约为《/2的表进行 排序,接下来再对这两个问题进行分割,使其成为4个对长度约为 n /4 的表进行排序的问题。这种 分割过程可以由图1246中的树结构来概括,图中树的每个节点表示的递归过程中的一个问题,并 且一个节点下面的分支表示的是从这个父节点衍生而来的更小的问题。所以,我们可以发现,将 树中各个节点上发生的比较次数加起来,就得到整个排序过程中所发生的总的比较次数。 对《个名 字的表排序 序 M 1 1 - 4 \ . 个/. 382 第 12 章计算理论 次数都不会多于《。(当然,最底层所包含的排序表的长度小于2,因而根本就不需要比较了。) 现在来确定树中的层数。为此,可以看到,把问题分割成更小的问题这个过程一直进行到 所得到表的长度小于2为止。这样一来,树中的层数就由分割的次数所确定,从值/7开始,反复 除以2,直到其结果不大于1,那么这个次数就是 Igw 。 更为准确地说,树中所涉及的比较层数不 多于,这里,标记表示的是将的值向上取整。 最后,把树中每层所做的比较次数乘以涉及比较操作的层数,这样就得到了在对长度为 n 的 表进行排序时,归并排序算法所做的总的比较次数。可以确定,这个次数不大于因为 对应的图形与对应的图形在形状上大致一样,我们就可以得出 结论: 归并排序算法 是属于 ooigw ) 的。把这个结论与研究人员告诉我们的排序问题的复杂性为 © oig «) 这个事实 相结合,这就意味着归并排序算法代表了排序问题的一个最优解。 遲麵 IPii 除了从睹间的角度来度量复杂性 ,: 还有一种方法就是通过度量 所需的 存储空间来衡量复 杂性,我们将这种度量方法称为空 向复杂性 ( space complexity )。也就是说,一个问題的空间 复杂性 禾由酶决该问题所霈的存储空:间的数量决定的^文中我们,已经看到,„ 一个有数椐项 _____.輸 插入排序对一个有《个数秦项的表进行排序,需要存放表本身的空间,还要加上用来存放临时 数据项的空间。这样一来,如:^要对越来越长的表进行排序,那么将会发现,每个任务所需的 时间比所需的空间增长要快得多。事实上,这是一个很常见的现象。爲为利用空间也要花费时 ■间,所以巧个问琿的空间裏杂彳生永远不舍比它的斤押复婦更快。 , r:j - 事先进行某些计算,并将计算结果嶙表格的形式存放起来,这样一来,在需要时就能通过表 格很 锋地检 _。这鮮一秤 >查表)的图像之上。例如,表达式 lg « 受表达式;?的约束(如图 12-17 a 所示),而《細受《 2 的约束 (如图 12-17 b 所示)。 ⑷/7与 lg « ( b ) h ) 与 《 lg « 图 12-17 数学表达式 w 、 lgw 、 的图像 12.5 问题的复杂性 383 如果一个问题是属于 0(/ ⑻)的,其中,表达式/⑻要么本身是一个多项式,要么就是受 —个 多项式约束,那么我们就说,这个问题是一 个多项式问题 (polynomial problem )。 所有多 项式问题的集合用 P 表示。注意,前面的讨论告诉我们,表的查找和排序问题就属于 P 。 说一个问题是多项式问题,这就是关于解决该问题所需时间的一种陈述。我们经常会说到, P 中的问题能够在多项式时间范围解决,或者说,该问题有多项式的时间解。 确定出属于 P 的问题是计算机科学中非常重要的课题,这是因为,这个问题与问题是否有实 际解密切相关。确实, P 类之外的问题,其特征都是具有极长的执行时间,即使是对中等规模的 输入也是如此。例如,考虑一个求解需要 2 n 步的问题。指数表达式 2" 不受任何多项式的约束, 也就是说,如果/⑻是一个多项式,那么,当增加《的值时,我们就会发现,2”的值最终会大于 /(«) 的值。这就意味着,如果一个复杂性为 0(2") 的算法通常会比复杂性 ©(/(«)) 的算法效率低, 因而就需要更多的时间。如果一个算法的复杂性是用指数表达式来确定的,那么就说该问题需 要指数时间。 下面介绍一个具体的例子,考虑这样一个问题,即从《个人组成的群体中,列出所有可能的 小组组合。因为这里可以有种这样的组合(这里可以允许一个小组包含所有的人,但是不 允许小组中没有人),所以解决此问题的任何算法必须至少有2^1步,这样一来,其复杂性也至 少这么大。但是,表达式2»_1作为一个指数表达式,不受任何多项式的约束。所以,随着可选 人群规模的增加,对这个问题的任何解所花费的时间也变得非常庞大。 上面的分组问题,其复杂性非常大,这只是因为它的输出规模太大,而与此不同的是,存 在着这样的一些问题,虽然它们最终的输出只是是或否这样简单,但是其复杂性却非常大 。一 个例子就是能不能回答涉及实数加法的一些语句的真实性问题。例如,对于“存在一个实数, 当自身相加时就得到值6。这是真的吗? ”这样的一个问题,我们就很容易得到答案,即答案为 真。而对“存在一个非零实数,当自身相加时就得到值0。这是真的吗? ”这样的一个问题,显 然答案为假。然而,当碰到这类问题越来越多时,我们回答这些问题的能力也就会开始减弱了。 如果发现自己要面对许多这样的问题,那么我们就可能尝试着求助于计算机程序。遗憾的是, 回答这类问题的能力已经证明需要指数时间,所以,随着所涉及的这类问题越来越多,最终的 结果是,即使是计算机也做不到以一种实时的方式给出答案。 理论上可解但不属于 P 的问题有着巨大的时间复杂性,这一事实使得我们得出 结论: 从实践 角度看,这些问题本质上是不可解的。计算机科学家称这类问题为 难解型 ( intractable ) 问题。 从而,类型 P 成为了用来区别难解的问题与那些可能有实际解的问题之间的一个重要分界线。因 此,对类型 P 的理解己经成为了计算机科学领域的一个重要研究内容。 12.5.3 NP 问题 现在来考 虑旅行商问题 (traveling salesman problem ), 该问题中涉及了一个旅行商,他必须 要访问到不同城市的每个客户,其花费不能超出他的出差预算。所以,他的问题就是要找到一 条路径(从家里出发,遍历有关的城市,然后.再返回),其总长度不超过允许的里程。 这个问题的传统解决办法是这样的,以系统化的方式来考虑各种可能的路径,然后把每条 路径的长度与里程的限制数相比较,直到找到一条可接受的路径,或者是把所有可能的路径都 考虑到。然而,这种方法并不能产生一个多项式时间解。随着城市数目的增加,所要测试的路 径数目比任何多项式增长得都要快。因此,在所涉及城市的数目很多的情况下,按照这种方式 来解决旅行商问题是不实际的。 我们可以得出 结论: 如果要在一个合理的时间范围内解决旅行商问题,必须要找到一个更 快的算法。如果存在着一个令人满意的路径,并且碰巧一开始就选择了这条路径,所提出的算 384 第 12 章计算理论 法也能很快地终止,那么这样就吊起了我们的胃口。具体来说,下面的指令序列能够很快地执 行,并且也有解决这个问题的 潜力: 取一个可能的路径,并计算其总距离 if (此距离不大于允许的里程数) then (宣布找到) else (宣布没找到) 然而,从技术意义上讲,这组指令不是一个算法。它的第一条指令就比较模糊,原因在于 它既没有指出选择的是哪一条路径,又没有说明如何作出这样的决定。相反,它是依赖于程序 执行机制的创造性来自己做决定。我们称这样的指令为不确定指令,并将包含这样语句的“算 法”称 为不确定算法 (nondeterministic algorithm )。 v 在许多情雖一如确 ,龙性 I :觸海” 爷本殚笔性、_条,’螞 '这赛塞谢是非▼清晰和 明昱和 4定裡算.不依綾于执行该筹鉍钤如造¥能诸 〔廣走算誇可能 ,会 依觀例如,比敕赭夺 1 _ ' O ,; 广.:, : : •. ^:::^;;,: :!••:; :;•'.' ;;::..::!• : i:-.;-;:ii : ; ... V:.;:::.:; : ,'r, • ;!': !;; i. i 卜个: :. .;•',• ;V ,; - : ::- ; '! :r.\ : : ! ::';: ::' ::". : ^''' ,,:; ':-:'' ; ' - 1 'I V; ;• ... K.: K: ^ 1 ■■;■;:;.:; :; j : ;':':;- : .: : ^';::;:; : y ; " ; ;:'^:;;;::;;;■?:■ ;/■,:■ ■:> ;:|^ " j /.;; ■:赵 :: :; | y :; ; ■::..;:■':■.: :: h ;;;■■■■;: ;:; >. ;;: ;:;:::: :: m :: ^:::^|::;;;::; j ; ; :' t :; g :; L 沒 }; .. :: 走到下一个十字雖口 > 熬后肉密转或 南左转 ’ , ' :: / : ; 1 : .. .':■•:•:•.:.•.!• •::: !• ::: !'• ': ::: •: :.. : :! ';• :; :' :!. -I:::.. 1 ': 1 . :;. ::. ' : -l .:; : :.;.:;. : ,,:••;,;;!':; ■■''' ;: :': •: : :: ;: •••;.! '■:.:■ S';.';.;.!' 1 ::. : : 乂. _.i... .i : ■' : -H :.■ : .;■ ■:'.:. ■:- . I\i i : :: : ; [ ' r , v':: ■/ :■ x ^"i -': !. : - :! ' :: !!:■■:; - : ■" : v:;.: ::: ;.,,■ : : ^ ■! :■ :: UK」::i-i V: e .-- !;■-::■:!■ i;,:-::;. :i ■■'!:; : !:!y :. .㈤ !..杂 :「■;: i::::: f ;-;!;;"; : - ......... • .;'•••;:::::':,;. •:.•;.: ;.;••:. :i,.: '• ' •' ' ;:; ':' ':• --- ... 乂 V..:.- •: :: r -:-: [*• •: - .•V- I .:.V:;:::!-.'V'. ;;•,,: ,.,.V :. .: ,r,:, v K '.',::::::;.,: ,: .;v,' ,.:•.';: .'-V, ' •.; : :••;:, ,:^;: ^,-,:,卜 . 与指令」 ' .. :: I ' . : 」 wm : 1 .. :;:..::::: XI :5 f :喷 a I . g :: :公 my 苦:免 / ii : "::::-.:' H :: ■^^;:^' VU ^ i :: 泛 货:鬥 遗 :适々 ㈤ :- ; •' :" ':• ;:' '• :'•;;: i ,' ;:•,! i, ::; ::,•:•:,;; :. G.:. .i; - ;.; ,.: .ijv'v,.. : ;-v .v:!',! ; : i."! -'iv ' !;' - -• : V ii i: ■:: .: ; :.:- ,.; v ; 7 ,.f :「卜 ;:;, ix ::. -••;!:; : : ::.' ..,.卜" 1 ..:.. .h A.WlU;;: ■ 走到下一个+字赂,曰,,,狻麁站在:路 ri 的人所隹街#的 L 向右转:或,._左鞋:/乂 :: ' t 1 .: : ▲逵南种褚进冲,远乎人在宾鼻执奸硪♦之前/所采取的肆為赫今幸讀袁:编:翁滅:痛: 一条指今秦求軒乂輕¥ It 戽的^ 來决定典转南卜所琢就是革鱗_讀蘇 人辦要 ¥的’方向魏不_ &濰 的要秦’搞: F 、 是被告备每二步•什•,命卞酵:狗多難 - 且得到:錡#的#息;: .疯轉 氺女衔蠢, :逡桌 4,舍 | J ■彰 象采繰 考乏渑的#臺聲罕赛、‘界_同# 妁 炎兔# ft 炒親輸:■:::_敏 熬淘,'工.蝻礴定-算在論鈦」 注意,随着城市数目的增加,执行上述不确定算法所需要的时间增加得相对较慢。选择一 条路径的过程仅仅是产生一个城市清单,这能够在与城市数目成比例的时间范围内完成。而且, 沿着所选择的路径,计算总距离所需的时间也与所访问的城市数目成正比,并且,将这个总数 与里程的限定数进行比较所需的时间与城市的数目无关。于是,执行这个不确定算法所需的 时间就受一个多项式约束。因此,就有可能在多项式时间内,利用一个不确定算法解决旅行商 问题。 当然,这个不确定解并不能令人十分满意,因为它依赖于猜测的运气。但是,它的存在足 以 表明: 在多项式时间内,对旅行商问题而言,存在着一个确定性的解。无论其真假与否, 它都是一个尚未确定的问题。事实上,有许多这样的问题,即知道他们在多项式时间范围内 执行时有不确定性解,但还没发现多项式时间内的确定性解,旅行商问题就这些问题中的一 个。对于这些问题的不确定性解的这种可望而不可及的效率,使得许多人希望在某一天能找 到有效的确定性解,然而,大麥数人相信这些问题太复杂,以致超出了有效的确定性算法的 能力范围。 12.5 问题的复杂性 385 一个能够在多项式时间内用不确定性算法解决的问题,称为 非确定性多项式问题 ( nondeter _ ministic polynomial problem ) ,或者简称 NP 问题 CNP problem ) c 习惯上把 NP 类问题表不成 NP 。 注意, P 中的所有问题也都属于 NP 问题,这是因为任何 C 确定性)算法都可以加上一条非确定 指令而不影响其性能。 然而,正如旅行商问题所说明的,所有的 NP 问题是否也都属于 P 问题还是个尚未确定的问 题。在今天,这也许是计算机科学领域里的最广为人知的未解问题。这个问题的解决将会带来 重大的影响。例如, 12.6 节我们将讨论到,已经设计出来的加密系统其完整性依赖于解决问题 所需的大量时间,这类似于旅行商问题。如果证实了对这样的问题存在着有效解,那么这些加 密系统的安全性就将受到威胁。 为了解决这样的问题(即 NP 类问题在事实上是否等同于 P 类问题)而作出的努力,导致了 在 NP 类问题中发现了一类称为 NP 完全问题 ( NP - completeproblem ) 的问题。这些问题都具有这 样一种特征,即任何一个问题的多项式时间解也为所有其他 NP 类问题提供了一个多项式时间 解。也就是说,如果能够在多项式时间内找到一个(确定)算法,使其能够解决一个 NP 完全问 题,那么这个算法就能推广到以多项式时间来解决任何其他于 NP 类问题。于是, NP 类就和 P 类 —样了。旅行商问题是 NP 完全问题的一个例子。 概括来说,可以看出,问题可以分为可解(有一个算法解)和不可解(没有算法解)两类, 如图 12-18 所示。而且,可解问题可分为两个子类。一类是多项式问题的集合,该集合包含了有 实际解的问题。另一类是非多项式问题的集合,这些问题只有当输入相对较少或者仔细选取的 情况下才有实际解。最后,还有比较难理解的 NP 问题,至今还没有很准确的分类。 可解问题 不可解问题 _I_I NP 问题 I 多项式 非多项 问题 式问题 图 12-18 问题分类的一个概括图 问题与练习 .. - • 1- 假设一个闾题能够通速一个属于 ©(20 的算法求解,那么对邀个问题的复杂性,我们能得出什么样的 结论 +? 1 _劈_柯薄|鮮^通过一个属于0(« 2 )的算法求解, 也可以通过另一个属于 ©,( 2 ")的箅法 求解,那么 - »:i ; !: :: ;; ':' ;:, :;i; 3. &壤__员会由灘俩个成员组成,那么请列出从 S 个委猿会的所有可能的分组。如果这个 委员^由 Alice 、 __細1组成,那么请列出从送个委员会的所有可能的分组。如果这个委员会是 由 Ali ^' JBill 、 Carol 賴赫 id 组成的,那么分组情况又会 g _? ' 4. 举出^个多项式间题的裯亭。举出一个非多项式问题的例 :季。 ‘举出一个尚南被证明是多项式问题的 NP 间题的例子。 5_如的复杂度比_?的复杂度要太,那么是否就意昧着算法 X — 定就比 算法 Y 难理解?请解释 你的^ 386 第 12 章计算理论 *12.6 公钥密码学 在某些情况下,一个问题难以解决这样一个事实已经不再是一个缺点,而变成了一个优点。 特别有趣的一个问题是,为给定的一个整数找到它的因数,如果这样的解确实存在,那么就一 定要为这个问题找到一个有效的解。例如,如果只用笔和纸,你将会发现,即使像2 173这样相 对较小的数值,要找到其因数也比较花时间,而如.果涉及的数大到需要用几百位数字来表示, 那么即使用现在最好的分解因数的技术,这个问题还是难以解决。 对许多数学家而言,至今还没有找到一种有效的方法来确定大整数的因数,这让他们感到 非常不安。然而,在密码学领域里,这种情况已经被用来产生一种对报文进行加密和解密的流 行方法。这种方法称为 RSA 算法 ( RSAalgorithm ), 选择这个名字是为了对该算法的发明者 Ron Rivest、Adi Shamir 和 Len Adleman 表示尊敬。这种方法,利用一组称为加 密密钥 (encrypting key ) 的数值对报文进行加密,并利用另一组称为解 密密钥 ( decryptingkey ) 的数值对报文进行解密。 知道加密密钥的人可以对报文进行加密,但是不能对报文进行解密。只有持有解密密钥的人才 能对报文进行解密。这样一来,加密密钥可以被广泛地分发而不会破坏系统的安全性。 这样的密码系统 称为公钥加密 ( public-key encryption ) 系统,这个术语反映了用来对报文 加密的密钥可以公开而不会降低系统的安全性。事实上,加密密钥通常被称 为公钥 (public key ), 而解密密钥则称 为私钥 (private key ) (如图 12-19 所示)。 图 12-19 公钥密码学 12.6.1 模表示法 为了描述 RSA 公钥加密系统,可以方便地采用记号 x(mod w ) 来表示数值 x 被除后所得的余数, 这通常读作 mod w ”。 这样一来, 9( mod 7) 就得2,这是因为9+7的余数为2。类似地, 24 (mod 7) 为3,这是因为24 + 7的余数为3; 14( mod 7) 为0,因为14+7的余数为0。注意,如果 x 是一个0〜/«-1 的整数,那么 x ( modw ) 的值就为 x 本身。例如, 4( mod 9) 的值就为4。 借助数学知识我们知道,如果;?和 q 是素数, m 是 Q 〜 pq (表示和^的乘积)的一个整数,那 么,对于任意的正整数 A :, 就有 1 = w * (广 1X9-1 > (mod 列) 尽管在这里不证明这个命题,但考虑一个例子来解释这个命题还是有必要的。于是,我们假设 p *12.6 公钥密码学 387 和分分别为素数3和5,并且为整数 4 。那么,这个命题说,对于任意的正整数 t 值除 以15 (3 和5的乘积)将得到余数1。具体来说,如果^1,那么 = 4 K3- i )(5-n = 4 s = 65 536 正如前面所讲的,该值除以15所得余数为1。而且, 如果& = 2,那么 m k { p -\)( q -\) = 4 2(3_1)(5 ~ 1) = 4 16 = 4 294 967 296 再将它除以15所得的余数为1。事实上,无论正整数 A: 的取值如何,都将得到余数1。 12.6.2 RSA 公钥加密系统 现在,我们准备在 RSA 算法的基础上构建和分析一个公钥加密系统。首先,选出两个不 同的素数 P 和^其乘积用《来表示。然后,另外选出两个正整数 e 和 A 使得对于某个正整数 t 满足将值 e 和 d 分别作为加密和解密过程的组成部分。(事实上,所选 取的值^和^/能够满足前面的等式,这在数学上也是另一个已经证明过的事实,在这里我们不 再证明。) 所以,这里选取了5 个值: e 和 A 值 e 和77是加密密钥,值^和是解密密钥 ,值 和3只是用来构建加密系统。 这里就考虑一个具体的例子来进行说明。假设将值 p 和 g 分别选取为7和13,那么 rv=n x 13 -91 0 而且,将值 e 和粉别选取为5和29,因为5 x 29=145=144 + 1 =2 ( 7-1 ) (13- 1 ) +1=2 ( p -1) 化-1)+1,这正是所需的。这样一来,加密密钥就是《=91和 e =5, 而解密密钥则为《=91和士29。我们将加密密钥分发给想要给我们发4艮文的人,而解密 密钥 ( 以及;?和 g 的值)我们自己保留。 我们现在来考虑如何对报文进行 加密。 为此,假设当前的报文是按照位模式(可能用的是 ASCII 编码或 Unicode 码)编码的,当将其解释为二进制表示时,这个位模式的值就 小于心 (如 果它不小于《,就要把报文分割成较小的段,然后分别对每段进行加密。) 假设当报文解释为二进制表示时,我们的报文表示值 m。 那么,这条报文的加密形式就 是值 c = (mod/?) 的二进制表示。也就是说,加密过的报文是 Y 除以《后所得余数的二进 制表示。 具体来说,继续就前面的例子来进行讨论,如果某人想要用加密密钥?? =91和 e = 5 对报文10111进行加密,他首先会看出,10111是值23的二进制表示,那么计算 23 e = 23 5 = 6 436 343,最后,将此值除以《 = 91,得到余数为4。所以这条文的加密形式就是 100,即为4的二进制表示。 为了解密一条用二进制记法表示的值为 c 的报文,就要计算〆 (mod«) 的值。也就是说,计 算〆的值,将结果再除以《,并保留所得的佘数。事实上,这个余数就是初始报文的值 m, 这 是因为 c d (mod n) = m exd (mod n) =(mod n) =m x (mod n) =/w(mod n) 388 第 12 章计算理论 正如前面所述,这里用到了 m k{p - l)(g - l) (modn) = (modpq) = \ , 以及 m (mod «) = w C 因 为 w 5) then (回答 ' yes ”) 复习题 391 else (挑一个比 5 小的数并将这个数作为答案) 34. 下面的算法是确定性的吗?请说明理由。 一直向前开。 在第三个十字路口问站。 在拐角处的人问他是应该往右还是往左。 根据那个人指的方向转弯。 开两个区,然后停下来。 35. 请确定下列算法中的非确定的 地方: 选1〜100的3个数。 if (如果所选数字之和大于 1 50) then (回答 “ yes ”) else (选择已选出的数中的一个,将该数 作为答案) 36. 下列算法是具有多项式时间复杂性还是具有 非多项式时间复杂性?请说明理由。 procedure mystery (ListOfNumbers) 从 ListOfNumbers 中选一组数 if (迭些数加起来得 125) then (回答 “ yes ”) else (不给出答案) 37. 以下问题中,哪些问题是属于 P 类的? a . 复杂性为的问题 b . 复杂性为3«的问题 c . 复杂性为 w 2 +2 w 的问题 d . 复杂性为《!的问题 38. 概述声明一个问题是多项式问题和声明一个 问题是非确定性多项式问题之间的区别。 39. 请举出一个问题的例子,要求该问题既属于 P 类,又属于 NP 类。 40. 假设给你两个算法用来解决同一个问题。一个 算法的时间复杂性为而另一个算法的时间 复杂性为4«,那么在什么样规模的输入上前者 比后者更为有效? 41. 假设要解决的问题是旅行商问题,其中所涉及 的城市数为15,而任意两个城市之间只有一条 路相连。那么请问,遍历这些城市共有多少种 不同的路径?这里假设每条路径的长度能够 在一微秒内计算出来,那么计算出所有这些路 轻的长度要花多长时间? 42. 如果将归并排序算法(如图 12-15 和图 12-4 所 示)应用到表 Alice 、 Bob 、 Carol 以及 David , 那么要做多少次名字比较?如果应用到表 Alice 、 Bob 、 Carol、David 以及 Elaine, 那么又 将做多少次名字比较? 43. 请为图 12-12 所示的每一类问题都举出一个 例子。 44. 设计一个算法,找到形如/+/ = «的等式的 整数解,这里,《是某个给定的正整数。并确 定你的算法的时间复杂性。 45. 背包问题 (knapsack problem ) 是另一个属于 NP 完全类的问题。该问题旨在从一个表中找 出一些数,使得这些数的和等于某个值。例 如,表 642 257 771 388 391 782 3 04 中的数据项257、388和782,它们的和为1 427。 请找出和为1 723的数据项。你用的是什么算 法?其复杂性又如何? 46. 请指出旅行商问题与背包问题的相似之处(参 见习题 45) 。 47. 下面的表排序算法称为冒泡排序。当将其用到 一个有《个数据项的表中时,冒泡排序需要在 表项中进行多少次比较? procedure BubbleSort(List) Counter 一 1 ; while (Counter 1) do (if(List 中的第 N 个项小于其前面的 项); then (第 N 个项和前面的一项交换) N — N-1 48. 用 RSA 公钥加密技术对报文110进行加密,这 里公钥为 k =91 和 e =5。 49. 用 RSA 公钥加密技术对报文 111 进行解密,这 里私钥为 P 133 和#5。 50. 假设你知道一个基于 RSA 算法的公钥加密系 统,其公钥为《 = 77和 e =7。 私钥是什么?怎 样才能让你在一个合理的时间范围内解决这 个问题? 51. 找出107 531的因数。这个问题与本章的内容 有什么关联? 52. 如果正整数《在2〜士范围内没有整数因子, 那么我们能够得出什么结论?对于找一个正 整数的因子这样的任务来说,这又能告诉你一 些什么? 392 第 12 章计算理论 社会问题 下面的问题有助于你分析一些与计算领域相关的道德、社会和法律问题。回答这些问题不 是唯一的目的,还应该考虑为什么这样回答,以及你的判断是否对每个问题都标准如一。 1. 假设一个问题的最优算法解将需要执行100年,那么你会认为这个问题是容易处理的 吗?为什么? 2. 公民有权在免受政府部门监控的情况下对报文进行加密吗?你的回答是否提供了 “合适 的”法律执行?那么谁来决定“合适的”法律执行是什么? 3. 如果人脑是一个算法设备,那么关于人性,图灵论题会有什么结果?在何种程度上你会 认为图灵机包含了人脑的计算能力? 4. 我们己经看到,不同的计算模型(如有限表、代数公式、图灵机等)有不同的计算能力。 不同的生物体也有不同的计算能力吗?不同的人的计算能力也不同吗?如果是这样,那 么具有较髙能力的人是否能用这些能力来获得更好的生活方式? 5. 在今天,有许多网站提供了大多数城市的交通地图。这些站点能够帮助寻找某个具体的 地址,并提供放大功能来看清小范围的街区布局。从这个事实出发,考虑下面一系列的 假想。假设这些地图站点配备了具有变焦功能的卫星拍照 技术。 假设这些变焦功能增强 到能对某个独立的建筑及其周边的景象给出更为详细的图像。假设这些图像又增强到包 括实时视频。假设这些实时视频图像通过使用红外线技术而得到加强。到了这个地步, 别人就能够一天24小时地监视你的家。在这一系列技术进步中,你的隐私权是在哪一点 开始受到侵犯的?在这一系列技术进步中,你认为什么事情上我们的做法超越了当今间 谍卫星技术的能力?在怎样的程度上,这个场景就只是假想的? 6. 假设一个公司开发了一个加密系统,并取得了专利权。政府机构是否有权以国家安全的 名义来使用这个系统?政府机构是否有权以国家安全的名义限制这个公司对这个系统 的商业使用?如果这个公司是跨国公司,那么情况又会怎样? 7. 假设你买了一个产品,其内部结构是加密的,那么你是否有权对这个产品的基本结构进 行解密?如果是,你是否有权以商业方式来使用这些信息?以非商业方式使用呢?如果 它的加密是利用一个秘密的加密系统来实现的,而你发现了这个秘密,那么你有权分享 这个秘密吗? 8. 很多年以前,哲学家约翰•杜威 (1859 一 1952) 提出了 “履责技术” ( responsibletechnology ) 这个术语。请举出一些例子,来说明你对“履责技术”是怎样理解的。在例子的基础上, 阐明你自己对“履责技术”的定义。在过去的100多年里,社会实践了 “履责技术”吗? 应当采取措施来保证它的实施吗?如果是,则应采取什么样的措施?如果不是,为什么? 课外阅读 Garey, M. R. and D. S. Johnson. Computers and Intractability. New York: W. H. Freeman, 1979. Hamburger, H. and D. Richards. Logic and Language Models for Computer Science. Englewood Cliffs, NJ: Prentice-Hall, 2002. Hofstadter, D. R. Gddel , Escher , Bach: An Eternal Golden Braid. St. Paul , MN: Vintage, 1980. Hopcroft, J. E. , R. Motwani, and J. D. Ullman. Introduction to Automata Theory, Languages, and Computation, 3rd ed. Boston, MA: Addison-Wesley, 2007. 课外阅读 393 Lewis, H. R. and C. H. Papadimitriou. Elements of the Theory of Computation , 2nd ed. Englewood Cliffs, NJ: Prentice-Hall, 1998. Rich E. Automata. Computability, and Complexity: Theory and Application. Upper Saddle River, NJ: Prentice-Hall, 2008. Sipser, M. Introduction to the Theory of Computation. Boston: PWS, 1996. Smith, C. and E. Kinber. Theory of Computing; A Gentle Introduction. Englewood Cliffs, NJ: Prentice-Hall, 2001. Sudkamp , T. A. Languages and Machines: An Introduction to the Theory of Computer Science , 3rd ed. Boston, MA: Addison-Wesley, 2006. ASCI 限 01011110 01011111 01100000 01100001 01100010 01100011 01100100 01100101 01100110 01100111 01101000 01101001 01101010 01101011 01101100 01101101 01101110 01101111 01110000 01110001 01110010 01110011 01110100 01110101 01110110 01110111 01111000 01111001 01111010 01111011 01111100 01111101 00001010 0A > 00111110 3E 00001011 0B ? 00111111 3F 00100000 20 @ 01000000 40 00100001 21 A 01000001 41 00100010 22 B 01000010 42 00100011 23 C 01000011 43 00100100 24 D 01000100 44 00100101 25 E 01000101 45 00100110 26 F 01000110 46 00100111 27 G 01000111 47 00101000 28 H 01001000 48 00101001 29 1 01001001 49 00101010 2A J 01001010 4A 00101011 2B K 01001011 4B 00101100 2C L 01001100 4C 00101101 2D M 01001101 4D 00101110 2E N 01001110 4E 00111111 2F 0 01001111 4F 00110000 30 P 01010000 50 00110001 31 Q 01010001 51 00110010 32 R 01010010 52 00110011 33 S 01010011 53 00110100 34 T 01010100 54 00110101 35 U 01010101 55 00110110 36 V 01010110 56 00110111 37 w 01010111 57 00111000 38 X 01011000 58 00111001 39 Y 01011001 59 00111010 3A z 01011010 5A 00111011 3B [ 01011011 5B 00111100 3C \ 01011100 5C 00111101 3D ] 01011101 5D T 面列出了 ASCII 码的一部分,并在每个位模式的左边都添加了一个0,以构成现在通 用的8位位模式。第3列是每个8位位模式对应的十六进制值。 符号 ASCII 十六进制值 符号 ASCII 十六进制值 符号 ASCII 十六进制值 EF01234567S9ABCDEF0123456789ABCD 5566666666 & 666666677777777777777 議!〃 S 2 3 4 5 6 8 9 处理二进制补码表示的电路 t 附录介绍用电路实现对二进制补码表示的值进行取负及相加操作。我们从图 B -1 的电 路开始,它将一个4位的二进制补码转换为该值的负值的表示。例如,假设给出3的二 进制补码表示,该电路则产生_3的表示。算法与正文中所述一样。也就是说,它自右向左复制 位模式,直至复制到一个1,然后当从左向右移动时,求剩余各位的补码。由于最右边 XOR 门 的一个输入值固定为0,所以此门只能将其另一个输入传送至输出。然而这个输出又向左传给下 一个 XOR 门作为一个输入。如果这个输出是1,那么下一个 XOR 门将会把其输入位取反后送给 输出。而且,这个1还会通过 OR 门向左传送,影响下一个门。就这样,复制给输出的第一个1 也会向左传送,使得所有剩余的位在送到输出时都取反。 输入 图 B -1 将一个二进制补码位模式取负的电路 接下来,我们考虑一下用二进制补码表示的两个数值的加法。具体而言,解决问题 + 0110 + 1011 时,我们是从右至左一列一列地计算,并且每列都执行相同的算法。因此,只要获得这类问题 一列的加法电路,通过重复这个单列电路,就可以构建许多列的相加电路。 多列加法问题中一个单列的相加算法是这 样的: 把当前列的两个数值相加,把相加的和加 上上一列的进位,并将此和的最低有效位记为答案,然后将进位传向下一列。图 B -2 中的电路遵 循这个算法。上面的 XOR 门决定了两个输入位的和。下面的 XOR 门将这个和与上一列的进位相 加。两个 AND 门及 OR 门一起把进位向左传送。具体来说,如果该列中最初的两个输入是1,或 者这些位的和及进位都是1,那么就会产生一个进位1。 图 B -3 表示单列电路的副本如何用于产生计算用4位二进制补码系统表示的两个值和的电 路。图中每个矩形表示单列加法电路的一个副本。需要注意 的是: 最右边矩形的进位值总是0, 396 附录 B 处理二进制补码表示的电路 因为它没有来自上一列的进位。以此类推,最左边矩形产生的进位将被忽略。 列的上 列的下 面一位 面一位 向下一



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3