出版时间:2011-10 出版社:人民邮电 作者:Peter Seibel 译者:田春
Tag标签:无
前言
译者序 很荣幸,我被授权翻译Practical Common Lisp一书。本书是自1994年Common Lisp语言标准化以来,国内出版的第一本Common Lisp的中文教材。 Lisp语言家族最早诞生于1959年,它是人类历史上第二个高级程序设计语言(第一个是Fortran)。那一年,人工智能(AI)专家John McCarthy发表了具有重大历史意义的第一篇LISP论文“Recursive Functions of Symbolic Expressions and their Computation by Machine, Part I”,其中介绍了一种运行在古老的IBM 704计算机上的列表处理语言LISP(LISt Processing,列表处理),借助它可以轻松描述当时人工智能领域用到的各种算法。从此,Lisp语言在包括AI领域在内的所有主流计算机分支上,都获得了长足的发展。Lisp平台不但在IBM PC出现之前的几乎所有计算机硬件体系上均有移植,甚至在20世纪80年代还出现过专门用来运行Lisp程序的硬件——Lisp机。1994年,ANSI标准化的Common Lisp语言将之前历史上的所有现存Lisp厂商的各种语言和平台特性做了一次伟大的总结,从此语言核心不再变化,不但标准化以前的历史遗留代码只通过少量修改就可以兼容现代Lisp平台,而且标准化以后写出的所有新代码也都几乎不经任何调整就可能运行在任何一种Common Lisp平台上,无论是带有原生或是字节码编译器的,还是间接转译成C语言的,或是运行在JVM上的。目前至少有13种不同的Common Lisp语言平台可以运行在现代计算机上,其中10种还在广泛使用中,远超过它们所在的操作系统上C和其他语言编译器的数量。可以说,Lisp语言家族长达50年的发展史就是整个计算机发展史的缩影。 我从2003年大学三年级时开始学习Common Lisp语言,至今已有八个年头。当时学习它的动机基本上是出于对人工智能(传统的逻辑和推理、知识表示等方向)的个人兴趣。不过随后很快就发现,Common Lisp是一门通用的编程语言,如果不考虑其历史渊源而只从语言本身的特性来观察的话,可以说它跟人工智能毫无关系。在Practical Common Lisp一书中,作者Peter Seibel也谈到这个问题。当今有太多的人对Lisp语言存在类似的误解,包括相当多学过早期Lisp语言的人还停留在列表(List)是Lisp语言的唯一复合数据类型的认识上。如果读者从头到尾学完了这本书,就会发现Common Lisp是一门特性丰富的大型编程语言,不但提供了现代编程语言普遍支持的各种数据类型(包括各种数值类型、字符串、数组、结构体和哈希表在内),还支持几乎所有的编程范式(面向过程的、函数式的以及面向对象的),尤其带有一套特性丰富且思想独到的面向对象编程接口CLOS(Common Lisp Object System)和OO扩展接口MOP(Meta-Object Protocol)。如果要用一句话来描述Common Lisp中的OO与C++/Java/SmallTalk等语言的OO有何不同,那就是Common Lisp对象系统完全不是基于消息传递的,而是基于广义函数的。有兴趣的读者应当仔细阅读本书的第16章和第17章,其中介绍了CLOS的一些入门内容。 不过Lisp语言最吸引人的地方还在于其与众不同的程序运行方式。从C语言一路学过来的人往往把一门语言的语法及其标准函数库视为语言的全部,因为一旦程序写好,编译器就会将整个代码编译成一个可执行程序或者被其他可执行程序使用的库。接下来语言本身是什么就不重要了,重要的是程序员写出了什么功能,甚至连编译器本身是什么都不重要,因为它只是一个黑箱,除了简单的优化开关之外几乎无法调整其行为。各种Lisp语言则采用完全不同的方式来运行Lisp程序:Lisp平台本身是一个交互式的环境,它在很大程度上就是用其本身写成的。用户的Lisp代码以编译或解释的形式加载到Lisp环境中,然后跟Lisp语言或平台本身的代码直接融合在一起。换句话说,每一个Lisp程序都是对Lisp语言本身的某种形式的扩展。然后通过一个启动函数,整个程序得以运行。听到这里,读者似乎看到了Python或者Ruby的影子,但Lisp环境还有更绝的地方:几乎所有Lisp平台都允许用户将加载了用户代码的整个环境从内存中导出(dump)为一个磁盘文件。通过直接加载这个文件而不是默认的那个只含有Lisp本身的文件,可以迅速地重建导出前的Lisp环境,从而达到增量开发或者哪怕是快速加载已有Lisp程序的目的。最后,和其他语言很不同的一点是,Lisp语言规范(至少Common Lisp是这样的)不但包括了如何定义某个程序组成部分(指的是变量、函数和类这些东西)的能力,还定义了从Lisp环境中清除任何程序组成部分以及就地修改它们的能力,并在语义和功能上确保了这些操作不会破坏运行中的Lisp代码。这导致了Lisp语言的另一个重要应用:通过加载补丁,Lisp系统可以在运行中被任意修改,这对24×7的服务器端程序的平滑升级尤为有利。顺便说一句,Lisp也是最早引入垃圾收集(GC)机制的编程语言,Lisp环境中的任何对象,一旦失去了来自其他对象的引用,就会在某个时刻被GC系统从内存中清除掉。 读者可能已经注意到了我在不停地混用Lisp和Common Lisp两个概念。这有两层含义:首先,存在Common Lisp之外的Lisp语言,更准确地说是Lisp方言(dialect),至少包括了Emacs Lisp、AutoLISP、Scheme、Racket(前身是PLT Scheme)和Clojure,其中最后一个是高速发展中的新兴Lisp方言;其次,所有Lisp家族的语言都有很多共性,除了上面描述中带有Lisp而非Common Lisp字样的部分以外,还有最大的也是初学者最容易看到的一点,那就是所有Lisp方言都使用前缀表达式和用小括号表示的列表,例如 1+1 在Lisp中将写成(+ 1 1)。很多初学者一开始都不适应前缀表达式,但我认为前缀表达式是有很多优点的:首先,它彻底消除了运算符结合性问题,令表达式毫无歧义可言;其次,它让语言处理器更加简单高效,避免了语法分析的困难。当然,一旦习惯了也就感觉没什么了。 学习本书对更好地使用其他Lisp方言无疑是大有帮助的。在翻阅书店里关于AutoLISP(AutoCAD计算机辅助设计软件的扩展语言)的各种书籍时,我经常痛心疾首地发现这些图书的作者虽然精通AutoCAD所提供的Lisp编程接口,但写出的AutoLISP代码要么极为难看,要么缺乏效率、滥用内存。AutoLISP在语法上跟Common Lisp非常接近,本书的大部分内容都适用于AutoLISP。因此我强烈推荐所有AutoLISP程序员阅读本书以加强自身的Lisp素养。同样的问题对于Emacs Lisp(GNU Emacs文本编辑器的扩展语言)来说也是一样的。Scheme系的Lisp方言区别相对大一些,如果连基本的变量和函数定义都在形式上完全不同的话(当然,思想上是没什么本质区别的),我恐怕初学者从本书中学得Scheme编程思想的机会不大,这种情况下还是推荐《计算机程序的构造和解释》、Lisp in Small Pieces和The Little Schemer等书籍比较好。 本书可以作为其他Common Lisp语言教材的学习基础。在本书的最后一章里,作者给出了很多后续的教材,在此就不一一重复了。需要特别指出的是,另一本著名的Common Lisp教材On Lisp(作者Paul Graham,也就是《黑客与画家》一书的作者)多年前已经被我和我的几位朋友共同翻译成中文版,细心的读者可以从网上轻易地找到它。On Lisp主要介绍Common Lisp的宏编程,这是Common Lisp区别于其他语言甚至其他Lisp方言的最重要特性。我相信一旦读者掌握了本书中关于宏的章节以后就可以阅读On Lisp中的进阶内容,从而将自身对编程语言的认识上升到一个新的高度,不过更加符合实用原则的思路还是先把本书读完。 Common Lisp绝不是一门过时的编程语言,整个Common Lisp社区一直都在高速的发展之中,近几年的发展尤为迅速。在我学习Common Lisp的这些年里,我亲眼目睹了几个Common Lisp平台从无到有(ECL、ABCL)或者发展壮大(SBCL、Clozure CL)的过程。经典平台(CMUCL、MCL)也得到了良好的维护并始终跟进操作系统的自然发展。随着计算机硬件的高速发展,即便相对保守的Common Lisp商业平台也开始或即将开始支持对称多处理器(SMP),其中LispWorks和Scieneer CL都以SMP支持作为主要卖点。第三方软件包长足发展,虽然尚未达到Perl社区CPAN的水平,但常用的工具包一应俱全,其中不乏高质量的大型项目。近年来最新的成果Quicklisp包管理平台,更是将Common Lisp第三方软件包的安装过程提升到了前所未有的便捷程度。免费平台越来越好,商业平台依然昂贵,开源工具蓬勃发展,所有这些都暗示着Common Lisp语言还保持着旺盛的生命力,唯一的问题是如何让更多的国内计算机领域爱好者了解它。这就是我翻译本书的目的所在。 过去8年里,我一直活跃在国内和国际Common Lisp社区的前沿。我在大学本科的最后两年学完了Common Lisp语言语法的主要部分,读完了包括本书在内的几本最经典的Lisp书籍,并已经能够在当时最常见的CMUCL平台(CMU Common Lisp)上编写一些简单的程序。后来在网易工作的5年里,我在工作之余从头研究了一遍Lisp语言的发展史,亲身体会了包括Lisp机在内的十几种不同的Common Lisp平台或实现,并自费购买了价值数千美元的商业开发环境LispWorks,拥用三种主流操作系统上的License。在网易从事Linux系统管理工作期间,我用Common Lisp从头实现了一万行源代码规模的SNMP简单网络管理协议工具包,它可以为任何服务器端Common Lisp程序添加通过SNMP协议进行远程监控的能力,也可以作为基于Common Lisp的网络监控系统的基础。我还在过去3年里参与维护了Common Lisp社区两个最重要的可移植网络库之一:usocket,并由于SNMP库的需要将其从原本只支持TCP扩展到了同时支持UDP,其中对于LispWorks的UDP支持代码是完全从头写的,因为官方并不支持。2009年,我向国际Lisp会议的投稿被接受,并作为会议论文集的一部分出版。我是长期担任水木社区函数型编程板块的板主之一,专门负责Lisp方向的讨论和技术分享。2011年7月,我离开网易以后开始全职从事商业Lisp软件相关的开发工作。可能我还不是一个很好的译者,但作为一个经验丰富的Common Lisp程序员,我相信自己翻译这本书是合适的。 计算机领域每天都在高速发展,新语言和新技术的产生速度早已超过了一般人的学习速度。对于一个计算机领域的从业人员或爱好者来说,学习通常是为了更好地应用,把所有时间都用来学习而无暇具体应用也是本末倒置。在这种情况下,有选择地学习最有用、最不易变质的知识,以及甄别各种计算机知识的重要程度和相互关系的能力就显得非常重要了。从计算机语言的发展历史来说,如果一门语言可以存活50年,那么它的内在生命力很可能保证其继续长期存活下去,一个人用这门语言写下的代码也将比其他语言的代码更有可能长久地造福后人。 总之,希望这本书能将读者顺利带入Lisp领域。学习一门新的语言总是要花些成本的,但我想说,和其他任何语言相比,花在理解Lisp上的时间和精力将绝对是物超所值的,即便相当多的读者可能没有机会在短期内将Lisp用于他们的日常工作。之所以这样说是有原因的:C和Lisp是编程语言的两个极端,大多数人已经熟悉了C的那一端,但如果他们还熟悉另一端的话,那么迅速理解几乎所有其他的编程语言将不再是问题。 译者简介 田春,网名“冰河”,Glority Software资深软件工程师,前网易杭州研究院高级开发工程师和系统管理员,资深Common Lisp程序员;2001~2005年就读于浙江大学,2003年起开始学习Commom Lisp,精通Lisp史和各种实现,2007年起成为 LispWorks 付费用户;Common Lisp社区的网络专家,开源项目cl-net-snmp(SNMP协议库)的作者,usocket跨平台网络库的主要维护者,common-lisp.net站点管理员,水木社区(newsmth.net)函数型编程语言(FuncProgram)版主,美国Versata/Gensym公司技术顾问;曾在ILC 2009(国际Lisp会议)上发表学术论文,在《程序员》杂志上发表Common Lisp专题文章;曾在2008年翻译了Paul Graham的On Lisp一书,并在网上撰写过大量相关的技术文章。
内容概要
《实用Common Lisp编程》是一本不同寻常的Common
Lisp入门书。《实用Common
Lisp编程》首先从作者的学习经过及语言历史出发,随后用21个章节讲述了各种基础知识,主要包括:REPL及Common
Lisp的各种实现、S-表达式、函数与变量、标准宏与自定义宏、数字与字符以及字符串、集合与向量、列表处理、文件与文件I/O处理、类、FORMAT格式、符号与包,等等。而接下来的9个章节则翔实地介绍了几个有代表性的实例,其中包含如何构建垃圾过滤器、解析二进制文件、构建ID3解析器,以及如何编写一个完整的MP3
Web应用程序等内容。最后还对一些未介绍内容加以延伸。
《实用Common Lisp编程》内容适合Common Lisp初学者及对之感兴趣的相关人士。
作者简介
Peter Seibel 从作家演变成程序员,又从程序员演变成作家,其职业生涯可谓一波三折。他在获得英语专业学士学位后做过一段时间的记者工作,后来被Web所吸引。在20世 纪90年代早期,他用Perl建立了Mother Jones杂志和Organic Online网站。他作为WebLogic的早期雇员参与了Java革命,随后又在加州大学伯克利分校教授Java编程。他也是第二代Lisp程序员之 一,并曾经是Symbolics的早期股东。2003年他辞去技术工作,潜心研究Lisp,并凭借本书获得Jolt生产效率大奖。2009年出版了名噪一 时的访谈录《编程人生》(Coders at Work)。
书籍目录
第1章 绪言:为什么是Lisp
1.1 为什么是Lisp
1.2 Lisp的诞生
1.3 本书面向的读者
第2章 周而复始:REPL简介
2.1 选择一个Lisp实现
2.2 安装和运行Lisp in a Box
2.3 放开思想:交互式编程
2.4 体验REPL
2.5 Lisp风格的“Hello, World”
2.6 保存工作成果
第3章 实践:简单的数据库
3.1 CD和记录
3.2 录入CD
3.3 查看数据库的内容
3.4 改进用户交互
3.5 保存和加载数据库
3.6 查询数据库
3.7 更新已有的记录——WHERE再战江湖
3.8 消除重复,获益良多
3.9 总结
第4章 语法和语义
4.1 括号里都可以有什么
4.2 打开黑箱
4.3 S-表达式
4.4 作为Lisp形式的S-表达式
4.5 函数调用
4.6 特殊操作符
4.7 宏
4.8 真、假和等价
4.9 格式化Lisp代码
第5章 函数
5.1 定义新函数
5.2 函数形参列表
5.3 可选形参
5.4 剩余形参
5.5 关键字形参
5.6 混合不同的形参类型
5.7 函数返回值
5.8 作为数据的函数——高阶函数
5.9 匿名函数
第6章 变量
6.1 变量的基础知识
6.2 词法变量和闭包
6.3 动态变量
6.4 常量
6.5 赋值
6.6 广义赋值
6.7 其他修改位置的方式
第7章 宏:标准控制构造
7.1 WHEN和UNLESS
7.2 COND
7.3 AND、OR和NOT
7.4 循环
7.5 DOLIST和DOTIMES
7.6 DO
7.7 强大的LOOP
第8章 如何自定义宏
8.1 Mac的故事:只是一个故事
8.2 宏展开期和运行期
8.3 DEFMACRO
8.4 示例宏:do-primes
8.5 宏形参
8.6 生成展开式
8.7 堵住漏洞
8.8 用于编写宏的宏
8.9 超越简单宏
第9章 实践:建立单元测试框架
9.1 两个最初的尝试
9.2 重构
9.3 修复返回值
9.4 更好的结果输出
9.5 抽象诞生
9.6 测试层次体系
9.7 总结
第10章 数字、字符和字符串
10.1 数字
10.2 字面数值
10.3 初等数学
10.4 数值比较
10.5 高等数学
10.6 字符
10.7 字符比较
10.8 字符串
10.9 字符串比较
第11章 集合
11.1 向量
11.2 向量的子类型
11.3 作为序列的向量
11.4 序列迭代函数
11.5 高阶函数变体
11.6 整个序列上的操作
11.7 排序与合并
11.8 子序列操作
11.9 序列谓词
11.10 序列映射函数
11.11 哈希表
11.12 哈希表迭代
第12章 LISP名字的由来:列表处理
12.1 “没有列表”
12.2 函数式编程和列表
12.3 “破坏性”操作
12.4 组合回收性函数和共享结构
12.5 列表处理函数
12.6 映射
12.7 其他结构
第13章 超越列表:点对单元的其他用法
13.1 树
13.2 集合
13.3 查询表:alist和plist
13.4 DESTRUCTURING-BIND
第14章 文件和文件I/O
14.1 读取文件数据
14.2 读取二进制数据
14.3 批量读取
14.4 文件输出
14.5 关闭文件
14.6 文件名
14.7 路径名如何表示文件名
14.8 构造新路径名
14.9 目录名的两种表示方法
14.10 与文件系统交互
14.11 其他I/O类型
第15章 实践:可移植路径名库
15.1 API
15.2 *FEATURES*和读取期条件化
15.3 列目录
15.4 测试文件的存在
15.5 遍历目录树
第16章 重新审视面向对象:广义函数
16.1 广义函数和类
16.2 广义函数和方法
16.3 DEFGENERIC
16.4 DEFMETHOD
16.5 方法组合
16.6 标准方法组合
16.7 其他方法组合
16.8 多重方法
16.9 未完待续
第17章 重新审视面向对象:类
17.1 DEFCLASS
17.2 槽描述符
17.3 对象初始化
17.4 访问函数
17.5 WITH-SLOTS和WITHACCESSORS
17.6 分配在类上的槽
17.7 槽和继承
17.8 多重继承
17.9 好的面向对象设计
第18章 一些FORMAT秘诀
18.1 FORMAT函数
18.2 FORMAT指令
18.3 基本格式化
18.4 字符和整数指令
18.5 浮点指令
18.6 英语指令
18.7 条件格式化
18.8 迭代
18.9 跳,跳,跳
18.10 还有更多
第19章 超越异常处理:状况和再启动
19.1 Lisp的处理方式
19.2 状况
19.3 状况处理器
19.4 再启动
19.5 提供多个再启动
19.6 状况的其他用法
第20章 特殊操作符
20.1 控制求值
20.2 维护词法环境
20.3 局部控制流
20.4 从栈上回退
20.5 多值
20.6 EVAL-WHEN
20.7 其他特殊操作符
第21章 编写大型程序:包和符号
21.1 读取器是如何使用包的
21.2 包和符号相关的术语
21.3 三个标准包
21.4 定义你自己的包
21.5 打包可重用的库
21.6 导入单独的名字
21.7 打包技巧
21.8 包的各种疑难杂症
第22章 高阶LOOP
22.1 LOOP的组成部分
22.2 迭代控制
22.3 计数型循环
22.4 循环集合和包
22.5 等价?然后迭代
22.6 局部变量
22.7 解构变量
22.8 值汇聚
22.9 无条件执行
22.10 条件执行
22.11 设置和拆除
22.12 终止测试
22.13 小结
第23章 实践:垃圾邮件过滤器
23.1 垃圾邮件过滤器的核心
23.2 训练过滤器
23.3 按单词来统计
23.4 合并概率
23.5 反向卡方分布函数
23.6 训练过滤器
23.7 测试过滤器
23.8 一组工具函数
23.9 分析结果
23.10 接下来的工作
第24章 实践:解析二进制文件
24.1 二进制文件
24.2 二进制格式基础
24.3 二进制文件中的字符串
24.4 复合结构
24.5 设计宏
24.6 把梦想变成现实
24.7 读取二进制对象
24.8 写二进制对象
24.9 添加继承和标记的结构
24.10 跟踪继承的槽
24.11 带有标记的结构
24.12 基本二进制类型
24.13 当前对象栈
第25章 实践:ID3解析器
25.1 ID3v2标签的结构
25.2 定义包
25.3 整数类型
25.4 字符串类型
25.5 ID3标签头
25.6 ID3帧
25.7 检测标签补白
25.8 支持ID3的多个版本
25.9 版本化的帧基础类
25.10 版本化的具体帧类
25.11 你实际需要哪些帧
25.12 文本信息帧
25.13 评论帧
25.14 从ID3标签中解出信息
第26章 实践:用AllegroServe进行Web编程
26.1 30秒介绍服务器端Web编程
26.2 AllegroServe
26.3 用AllegroServe生成动态内容
26.4 生成HTML
26.5 HTML宏
26.6 查询参数
26.7 cookie
26.8 小型应用框架
26.9 上述框架的实现
第27章 实践:MP3数据库
27.1 数据库
27.2 定义模式
27.3 插入值
27.4 查询数据库
27.5 匹配函数
27.6 获取结果
27.7 其他数据库操作
第28章 实践:Shoutcast服务器
28.1 Shoutcast协议
28.2 歌曲源
28.3 实现Shoutcast
第29章 实践:MP3浏览器
29.1 播放列表
29.2 作为歌曲源的播放列表
29.3 操作播放列表
29.4 查询参数类型
29.5 样板HTML
29.6 浏览页
29.7 播放列表
29.8 查找播放列表
29.9 运行应用程序
第30章 实践:HTML生成库,解释器部分
30.1 设计一个领域相关语言
30.2 FOO语言
30.3 字符转义
30.4 缩进打印器
30.5 HTML处理器接口
30.6 美化打印器后台
30.7 基本求值规则
30.8 下一步是什么
第31章 实践:HTML生成库,编译器部分
31.1 编译器
31.2 FOO特殊操作符
31.3 FOO宏
31.4 公共API
31.5 结束语
第32章 结论:下一步是什么
32.1 查找Lisp库
32.2 与其他语言接口
32.3 让它工作,让它正确,让它更快
32.4 交付应用程序
32.5 何去何从
编辑推荐
由塞贝尔编著的《实用Common Lisp编程》展示了Lisp的威力,不仅表现在传统领域上,例如仅使用短短26行代码就开发出一个完整的单元测试框架,而且还表现在一些全新的领域上,诸如解析二进制MP3文件、构建浏览歌曲集的Web应用、在Web上传播音频流等。读过本书,你将体会到Lisp具有Pytl-ion等脚本语言的简洁性、C++的高效性,以及在设计语言扩展时无与伦比的灵活性。 《实用Common Lisp编程》内容适合Common Lisp初学者及对之感兴趣的相关人士。
图书封面
图书标签Tags
无
评论、评分、阅读与下载