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