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