贾大师 2009-09-03
本文是paul graham所著的《Lisp之根源》的第一部分,在对Lisp的介绍中,描述了Lisp中的七个原始操作符。这是一篇非常不错的Lisp介绍文,值得一读。
约翰麦卡锡于1960年发表了一篇非凡的论文,他在这篇论文中对编程的贡献有如欧几里德对几何的贡献.1 他向我们展示了,在只给定几个简单的操作符和一个表示函数的记号的基础上, 如何构造出一个完整的编程语言. 麦卡锡称这种语言为Lisp, 意为List Processing, 因为他的主要思想之一是用一种简单的数据结构表(list)来代表代码和数据.
值得注意的是,麦卡锡所作的发现,不仅是计算机史上划时代的大事, 而且是一种在我们这个时代编程越来越趋向的模式.我认为目前为止只有两种真正干净利落, 始终如一的编程模式:C语言模式和Lisp语言模式.此二者就象两座高地, 在它们中间是尤如沼泽的低地.随着计算机变得越来越强大,新开发的语言一直在坚定地趋向于Lisp模式. 二十年来,开发新编程语言的一个流行的秘决是,取C语言的计算模式,逐渐地往上加Lisp模式的特性,例如运行时类型和无用单元收集.
在这篇文章中我尽可能用最简单的术语来解释约翰麦卡锡所做的发现. 关键是我们不仅要学习某个人四十年前得出的有趣理论结果, 而且展示编程语言的发展方向. Lisp的不同寻常之处--也就是它优质的定义--是它能够自己来编写自己. 为了理解约翰麦卡锡所表述的这个特点,我们将追溯他的步伐,并将他的数学标记转换成能够运行的Common Lisp代码.
七个原始操作符
开始我们先定义表达式 .表达式或是一个原子 (atom),它是一个字母序列(如 foo),或是一个由零个或多个表达式组成的表 (list), 表达式之间用空格分开, 放入一对括号中. 以下是一些表达式:
foo () (foo) (foo bar) (a b (c) d)
最后一个表达式是由四个元素组成的表, 第三个元素本身是由一个元素组成的表.
在算术中表达式 1 + 1 得出值2. 正确的Lisp表达式也有值. 如果表达式e 得出值v ,我们说e 返回 v . 下一步我们将定义几种表达式以及它们的返回值.
如果一个表达式是表,我们称第一个元素为操作符 ,其余的元素为自变量 .我们将定义七个原始(从公理的意义上说)操作符: quote,atom,eq,car,cdr,cons,和 cond.
(quote x ) 返回x .为了可读性我们把(quote x )简记 为'x . Lisp代码
> (quote a) a > 'a a > (quote (a b c)) (a b c)
(atom x )返回原子t如果x 的值是一个原子或是空表,否则返回(). 在Lisp中我们按惯例用原子t表示真, 而用空表表示假. Lisp代码
> (atom 'a) t > (atom '(a b c)) () > (atom '()) t
既然有了一个自变量需要求值的操作符, 我们可以看一下quote的作用. 通过引用(quote)一个表,我们避免它被求值. 一个未被引用的表作为自变量传给象 atom这样的操作符将被视为代码:
> (atom (atom 'a)) t
反之一个被引用的表仅被视为表, 在此例中就是有两个元素的表:
> (atom '(atom 'a)) ()
这与我们在英语中使用引号的方式一致. Cambridge(剑桥)是一个位于麻萨诸塞州有90000人口的城镇. 而``Cambridge''是一个由9个字母组成的单词.
引用看上去可能有点奇怪因为极少有其它语言有类似的概念. 它和Lisp最与众不同的特征紧密联系:代码和数据由相同的数据结构构成, 而我们用quote操作符来区分它们.
(eq x y )返回t如果x 和y 的值是同一个原子或都是空表, 否则返回(). Lisp代码
> (eq 'a 'a) t > (eq 'a 'b) () > (eq '() '()) t
(car x )期望x 的值是一个表并且返回x 的第一个元素. Lisp代码
> (car '(a b c)) a
(cdr x )期望x 的值是一个表并且返回x 的第一个元素之后的所有元素.
> (cdr '(a b c)) (b c)
(cons x y )期望y 的值是一个表并且返回一个新表,它的第一个元素是x 的值, 后面跟着y 的值的各个元素. Lisp代码
> (cons 'a '(b c)) (a b c) > (cons 'a (cons 'b (cons 'c '()))) (a b c) > (car (cons 'a '(b c))) a > (cdr (cons 'a '(b c))) (b c)
(cond ( ... ) ...( ... )) 的求值规则如下. p 表达式依次求值直到有一个返回t. 如果能找到这样的p 表达式,相应的e 表达式的值作为整个cond表达式的返回值.
> (cond ((eq 'a 'b) 'first) ((atom 'a) 'second)) second