Lisp 程序代码与数据的形式完全相同,这使得它非常强大,能完成许多其他语言不能完成的事情。为了拥有这个强大的特性,我们需要将求值过程分为读取并存储输入、对输入进行求值两个过程。
本章结束后,程序的运行结果会和上一章的有轻微的不同。这是因为我们会花时间去更改程序内部的工作方式。在软件开发中,这被叫做重构。重构可能对于当前的程序运行结果并没有太大的影响,但因为工作方式的优化,会使我们在后面的开发中更加省心。
为了存储输入,我们需要创建一个内部列表结构,能够递归的表示数字、操作符号、其他的列表。在 Lisp 中,这个结构被称为 S-表达式(Symbolic Expression)。我们将扩展 lval
结构来表示它。S-表达式求值也是典型的 Lisp 式过程:首先取列表第一个元素为操作符,然后遍历所有剩下的元素,作为操作数。
有了 S-表达式,我们才算真正迈进了 Lisp 的大门。
在 C 语言中,要表示列表,就必须正确的使用指针。C 语言中的指针一直如洪水猛兽般存在。虽然概念上非常简单,但是用起来却变幻多端,神秘莫测,这使得指针看上去比实际要可怕得多。幸运的是,在本书中我们只会用一些指针在 C 语言中最常规的用法。
我们之所以需要指针,主要是由 C 语言中函数的工作方式决定的。C 语言函数的参数都是通过值传递的。也就是说,传递给函数的是实参的拷贝。对于 int
、long
、char
等系统类型以及用户自定义的结构体都是成立的。这种方式适用于绝大多数情况,但也会偶尔出现问题。
一种常见的情况是,当我们有一个巨大结构体需要作为参数传递的时候,每次调用函数,就会对实参进行一次拷贝,这无疑是对性能和内存的浪费。