LISP 的与众不同之处
Original2001 年 12 月(2002 年 5 月修订)
(本文是为了回答LL1邮件列表中的一些问题而撰写的。它现在被收录在《书呆子的复仇》中。)
当麦卡锡在 20 世纪 50 年代末设计 Lisp 时,它与当时的众多语言(其中最重要的是Fortran)有着彻底的不同。
Lisp 体现了九个新思想:
**1. 条件句。**条件句是一种 if-then-else 结构。我们现在认为这些是理所当然的。它们是 McCarthy 在开发 Lisp 的过程中发明的。(当时 Fortran 只有条件 goto,与底层硬件中的分支指令密切相关。)McCarthy 是 Algol 委员会的成员,他将条件句引入了 Algol,从此它们传播到了大多数其他语言。
**2. 函数类型。**在 Lisp 中,函数是第一类对象——它们是一种数据类型,就像整数、字符串等一样,具有文字表示,可以存储在变量中,可以作为参数传递,等等。
**3. 递归。**递归在 Lisp 之前就已经存在,但 Lisp 是第一个支持递归的编程语言。(可以说,函数成为第一类对象是隐含的。)
**4. 变量的新概念。**在 Lisp 中,所有变量实际上都是指针。值才是具有类型的,而不是变量,赋值或绑定变量意味着复制指针,而不是它们指向的内容。
5.垃圾收集。
6. 由表达式组成的程序。Lisp程序是表达式树,每个表达式都返回一个值。(在某些 Lisp 中,表达式可以返回多个值。)这与 Fortran 和大多数后续语言不同,后者区分了表达式和语句。
Fortran 中存在这种区别是很自然的,因为(在输入格式为打孔卡的语言中,这并不奇怪)该语言是面向行的。您无法嵌套语句。因此,虽然您需要表达式才能进行数学运算,但让其他任何东西返回值是没有意义的,因为不可能有任何东西在等待它。
随着块结构语言的出现,这一限制消失了,但那时已经太迟了。表达式和语句之间的区别已经根深蒂固。它从 Fortran 传播到 Algol,然后又传播到它们的后代。
当一种语言完全由表达式组成时,你可以随心所欲地编写表达式。你可以说(使用Arc语法)
(如果 foo(= x 1)(= x 2))
或者
(= x(如果 foo 1 2))
**7. 符号类型。**符号与字符串的不同之处在于,可以通过比较指针来测试相等性。
8. 使用符号树的代码表示法。
**9. 整个语言始终可用。**读取时、编译时和运行时之间没有真正的区别。您可以在读取时编译或运行代码,在编译时读取或运行代码,以及在运行时读取或编译代码。
在读取时运行代码让用户可以重新编程 Lisp 的语法;在编译时运行代码是宏的基础;在运行时编译是 Lisp 在 Emacs 等程序中用作扩展语言的基础;而在运行时读取使程序能够使用 s 表达式进行通信,这个想法最近被重新定义为 XML。
当 Lisp 刚刚被发明时,所有这些想法都与普通的编程实践相去甚远,而普通的编程实践很大程度上是由 20 世纪 50 年代后期的硬件决定的。
随着时间的推移,默认语言(体现在一系列流行语言中)逐渐向 Lisp 演变。1-5 现在已广泛使用。6 开始出现在主流中。Python 有 7 的形式,尽管它似乎没有任何语法。8(与 9 一起)使 Lisp 宏成为可能,到目前为止仍然是 Lisp 独有的,可能是因为(a)它需要那些括号,或者其他同样糟糕的东西,并且(b)如果你添加最后的力量增量,你就不能再声称自己发明了一种新语言,而只能说设计了一种新的 Lisp 方言 ;-)
虽然对当今的程序员来说 Lisp 很有用,但用它与其他语言所采用的随机权宜之计来描述 Lisp 却很奇怪。麦卡锡可能不是这么想的。Lisp 的设计目的并不是修复 Fortran 中的错误;它更像是一种试图将计算公理化的副产品。