LISP的不同之处
Original2001年12月 (修订于2002年5月)
(本文是对LL1邮件列表上的一些问题的回应。它现已被收录在Revenge of the Nerds中。)
当麦卡锡在20世纪50年代末设计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,再传播到它们的后代。
当一种语言完全由表达式组成时,你可以以任何你想要的方式组合表达式。你可以说:
(if foo (= x 1) (= x 2))
或者
(= x (if 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的错误;它更多是作为一种计算公理化尝试的副产品而产生的。