没有合适的资源?快使用搜索试试~ 我知道了~
首页Lisp之根源(初学则必看)手册
Lisp之根源(初学则必看)手册
需积分: 9 168 浏览量
更新于2023-03-03
评论 4
收藏 110KB DOC 举报
保罗格雷厄姆(Paul Graham) 约翰麦卡锡于1960年发表了一篇非凡的论文,他在这篇论文中对编程的贡献有如欧几里德对几何的贡献.1 他向我们展示了,在只给定几个简单的操作符和一个表示函数的记号的基础上, 如何构造出一个完整的编程语言. 麦卡锡称这种语言为Lisp, 意为List Processing, 因为他的主要思想之一是用一种简单的数据结构表(list)来代表代码和数据. 值得注意的是,麦卡锡所作的发现,不仅是计算机史上划时代的大事, 而且是一种在我们这个时代编程越来越趋向的模式.我认为目前为止只有两种真正干净利落, 始终如一的编程模式:C语言模式和Lisp语言模式.此二者就象两座高地, 在它们中间是尤如沼泽的低地.随着计算机变得越来越强大,新开发的语言一直在坚定地趋向于Lisp模式. 二十年来,开发新编程语言的一个流行的秘决是,取C语言的计算模式,逐渐地往上加Lisp模式的特性,例如运行时类型和无用单元收集. 在这篇文章中我尽可能用最简单的术语来解释约翰麦卡锡所做的发现. 关键是我们不仅要学习某个人四十年前得出的有趣理论结果, 而且展示编程语言的发展方向. Lisp的不同寻常之处--也就是它优质的定义--是它能够自己来编写自己. 为了理解约翰麦卡锡所表述的这个特点,我们将追溯他的步伐,并将他的数学标记转换成能够运行的Common Lisp代码.
资源详情
资源评论
资源推荐

Lisp 之根源
保罗格雷厄姆(Paul Graham)
约翰麦卡锡于 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.
1 (quote x) 返回 x.为了可读性我们把(quote x)简记 为'x.
> (quote a)
a
> 'a
a
> (quote (a b c))
(a b c)
2 (atom x)返回原子 t 如果 x 的值是一个原子或是空表,否则返回(). 在 Lisp 中我们按惯

例用原子 t 表示真, 而用空表表示假.
> (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 操作符来区分它们.
3 (eq x y)返回 t 如果 x 和 y 的值是同一个原子或都是空表, 否则返回().
> (eq 'a 'a)
t
> (eq 'a 'b)
()
> (eq '() '())
t
4 (car x)期望 x 的值是一个表并且返回 x 的第一个元素.
> (car '(a b c))
a
5 (cdr x)期望 x 的值是一个表并且返回 x 的第一个元素之后的所有元素.
> (cdr '(a b c))
(b c)
6 (cons x y)期望 y 的值是一个表并且返回一个新表,它的第一个元素是 x 的值, 后面跟
着 y 的值的各个元素.
> (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)
7 (cond (...) ...(...)) 的求值规则如下. p 表达式依次求值直到有一个返回 t. 如果能找
到这样的 p 表达式,相应的 e 表达式的值 作为整个 cond 表达式的返回值.
> (cond ((eq 'a 'b) 'first)
((atom 'a) 'second))
second
当表达式以七个原始操作符中的五个开头时,它的自变量总是要求值的.
2
我们称这样 的操作
符为
函数
.
函数的表示
接着我们定义一个记号来描述函数.函数表 示为(lambda (...) e),其中 ...是原子(叫做
参数
),
e 是表达式. 如果表达式的第一个元素形式 如上
((lambda (...) e) ...)
则称为
函数调用
.它的值计算如下.每一个表 达式先求值,然后 e 再求值.在 e 的求值过程
中,每个出现在 e 中的的值是相应的在最近 一次的函数调用中的值.
> ((lambda (x) (cons x '(b))) 'a)
(a b)
> ((lambda (x y) (cons x (cdr y)))
'z
'(a b c))
(z b c)
如果一个表达式的第一个元素 f 是原子且 f 不是原始操作符
(f ...)
并且 f 的值是一个函数(lambda (...)),则以上 表达式的值就是
((lambda (...) e) ...)
的值. 换句话说,参数在表达式中不但可以作 为自变量也可以作为操作符使用:
> ((lambda (f) (f '(b c)))
'(lambda (x) (cons 'a x)))
(a b c)
有另外一个函数记号使得函数能提及它本身,这样我们就能方便地定义递归函数.
3
记号
(label f (lambda (...) e))
表示一个象(lambda (...) e)那样的函数,加上 这样的特性: 任何出现在 e 中的 f 将求值为
此 label 表达式, 就好象 f 是此函数的参数.
假设我们要定义函数(subst x y z), 它取表达式 x,原子 y 和表 z 做参数,返回一个象 z 那样的表,
不过 z 中出现的 y(在任何嵌套层次上)被 x 代替.
> (subst 'm 'b '(a b (a b c) d))
剩余10页未读,继续阅读


















安全验证
文档复制为VIP权益,开通VIP直接复制

评论0