本书适用于那些想更上一层楼的 Lisp 程序员。书中假设读者已经初步了解 Lisp, 但不要求有丰富的编程经验。最初几章里会重温很多基础知识。我希望这些章节也会让有经验的Lisp 程序员感兴趣, 因为它们以崭新的视角展示了熟知的主题。
通常很难一语道清一门编程语言的精髓, 但 John Foderato 的话已经很贴切了:
> Lisp 是一门可编程的编程语言。
>
> (Lisp is a programmable programming language.)
这难免以偏概全, 但这种让 Lisp 随心而变的能力, 在很大程度上正是 Lisp 专家和新手的不同之处。在自上而下, 把程序逐渐具体化, 用编程语言实现设计的同时, 资深的 Lisp 程序员也实践着自底向上的方法, 他们通过创建语言来描述程序的行为。本书教授自底向上编写程序的方法, 因为这是 Lisp 与生俱来的强项。
## 自底向上的设计(Bottom-up Design)
随着软件复杂度的增长, 自底向上设计的重要性也日益提高。今天的程序可能不得不面对极其复杂甚至开放式的需求。在这种情况下, 传统的自上而下方法有时会失效。一种新的编程风格应运而生, 它和当前大部分计算机科学课程的思路截然不同:
> 一个自底向上的程序由一系列的层写成, 每一层都作为更高一层的编程语言。
>
> X-Window 和 TeX 就是这种程序设计风格的典范。
本书有两层主题:
首先, 对以自底向上的方法编制的程序来说, Lisp 语言是不二之选, 反过来, 编写 Lisp 程序的话, 采用自底向上的编程风格也是理所当然的。因此《On Lisp》将吸引两类读者。对于那些有兴趣编写可扩展程序的人, 本书将告诉你如果有了合适的语言, 你能做些什么。对于Lisp 程序员来说, 本书提供了第一手的实践指南, 指引他们把 Lisp 的优势发挥到极致。
本书选用现在的这个书名是为了强调自底向上编程对于 Lisp 的重要性。你不再仅仅是用 Lisp 编写程序, 在 Lisp 之上(OnLisp) , 你可以构造自己的语言, 然后再用这个语言来写程序。
尽管用任何语言都可以写出自底向上风格的程序, 但 Lisp 对于这种编程风格来说是最自然的载体。在 Lisp 里, 自底向上的设计并不是那种仅为少见的大型程序或者高难程序服务的专门技术。任何规模的程序都可以在一定程度上以这种方式编写。Lisp 从一开始就被设计成可扩展的语言。这种语言本身基本上就是一个Lisp 函数的集合, 这些函数和你自己定义的没有本质区别。更进一步, Lisp 函数可以表达成列表, 而列表同时也是Lisp 的数据结构。这就意味着你可以写出能生成Lisp 代码的Lisp 函数。
一个好的 Lisp 程序员必须懂得如何利用上述这种可能性。通常的途径是定义一种称为宏的操作符。驾驭宏是从编写正确的Lisp 程序走向编写漂亮的程序过程中最重要的一步。入门级Lisp 书籍给宏留下的篇幅仅限于一个宏的简短的概述: 解释一下宏是什么, 加上几个例子蜻蜓点水地提一下, 说能用它实现一些奇妙的东西。不过本书会给予这些奇妙的东西特别的重视。这里的目标之一就是把所有关于宏的知识作一次总结, 在以往, 人们只能从使用宏的经验和教训中来吸取这些知识。
一般来说, Lisp 的入门读物都不会强调 Lisp 和其他语言的区别, 这情有可原。它们必须想办法把知识传授 给那些被教育成只会用 Pascal 术语来构思程序的学生。如果非要细究这些区别的话, 只会把问题复杂化:
例如 defun 虽然看起来像一个过程定义, 但实际上, 它是一个编写程序的程序, 这个程序生成了一段代码, 而这段代码新建了一个函数对象, 然后用函数定义时给出的第一个参数作为这个函数对象的索引。
本书的目的之一就是解释究竟是什么使Lisp 不同于其他语言。刚落笔时, 我心里明白, 同等条件下自己会更倾向于用Lisp 而不是 C, Pascal 或 Fortran 来写程序。我也知道这不只是个人好恶的问题。但当意识到就要郑重其事地告诉大家 Lisp 语言在某些方面更优秀时, 我发现应该做好准备, 说说到底为什么。
曾有人问Louis Armstrong 什么是爵士乐, 他答道 "如果你问爵士乐是什么, 那你永远不会知道。" 但他确实以一种方式回答了这个问题:他向世人展示了什么是爵士乐。同样也只有一种方式来解释Lisp 的威力, 就是演示那些对于其他语言来说极其困难甚至不可能实现的技术。多数关于编程的书籍, 包括 Lisp 编程书籍, 采用的都是那些你可以用任何其它语言编写的程序。《On Lisp》涉及的多是那些只能用 Lisp 写的程序。
可扩展性, 自底向上程序设计, 交互式开发, 源代码转换, 嵌入式语言. 这些都是Lisp 展示其高级特性的舞台。
当然从理论上讲, 任意图灵等价的编程语言能做的事, 其它任何语言都可以做到。但这种能力和编程语言的能力却完全是两码事。理论上, 任何你能用编程语言做到的事, 也可以用图灵机来做, 但实际上在图灵机上编程得不偿失。
所以, 当我说这本书是关于如何做那些其他语言力所不及的事情的时候, 我并非指数学意义上的 "不可能", 而是从编程语言的角度出发的。这就是说, 如果你不得不用 C 来写本书中的一些程序, 你可能需要先用 C 写一个 Lisp 编译器。举个例子, 在 C 语言里嵌入 Prolog 你能想象这需要多少工作量吗 第 24 章将 说明如何用 180 行 Lisp 做到这一点。
尽管我希望能比单单演示Lisp 的强大之处做得更多。我也想解释为何 Lisp 与众不同。这是一个更微妙的问题, 这个问题是那么难回答, 它无法使用诸如 "符号计算" 这样的术语来搪塞。我将尽我所学, 尽可能清楚明白地解释这些问题。
- 封面
- 译者序
- 前言
- 第 1 章 可扩展语言
- 第 2 章 函数
- 第 3 章 函数式编程
- 第 4 章 实用函数
- 第 5 章 函数作为返回值
- 第 6 章 函数作为表达方式
- 第 7 章 宏
- 第 8 章 何时使用宏
- 第 9 章 变量捕捉
- 第 10 章 其他的宏陷阱
- 第 11 章 经典宏
- 第 12 章 广义变量
- 第 13 章 编译期计算
- 第 14 章 指代宏
- 第 15 章 返回函数的宏
- 第 16 章 定义宏的宏
- 第 17 章 读取宏(read-macro)
- 第 18 章 解构
- 第 19 章 一个查询编译器
- 第 20 章 续延(continuation)
- 第 21 章 多进程
- 第 22 章 非确定性
- 第 23 章 使用 ATN 分析句子
- 第 24 章 Prolog
- 第 25 章 面向对象的 Lisp
- 附录: 包(packages)