Loading...

LISP 的与众不同之处

Original

December 2001 (rev. May 2002)

(这篇文章是针对 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,然后传播到它们的子孙。

当一种语言完全由表达式组成时,你可以随意组合表达式。你可以说(使用 Arc 语法)

(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 中的错误;它更像是对 计算公理化 的尝试的副产品。