一个要求值的 lisp 对象被称为表达式(form)。

  • 第一种表达式是最简单的,自求值表达式。前面说过数字、字符串、向量都是自求值表达式。还有两个特殊的符号 t 和 nil 也可以看成是自求值表达式。
  • 第二种表达式是符号。符号的求值结果就是符号的值。如果它没有值,就会出现 void-variable 的错误。
  • 第三种表达式是列表表达式。而列表表达式又可以根据第一个元素分为函数调用、宏调用和特殊表达式( special form)三种。列表的第一个表达式如果是一个符号,解释器会查找这个表达式的函数值。如果函数值是另一个符号,则会继续查找这个符号的函数值。这称为“symbol function indirection”。最后直到某个符号的函数值是一个 lisp 函数(lambda 表达式)、byte-code 函数、原子函数( primitive function)、宏、特殊表达式或 autoload 对象。如果不是这些类型,比如某个符号的函数值是前面出现的某个符号导致无限循环,或者某个符号函数值为空,都会导致一个错误 invalid-function。

这个函数显示 indirection function:

1
2
3
4
(symbol-function 'car)                  ; => #<subr car>
(fset 'first 'car) ; => car
(fset 'erste 'first) ; => first
(erste '(1 2 3)) ; => 1

对于第一个元素是 lisp 函数对象、byte-code 对象和原子函数时,这个列表也称为函数调用( funtion call)。对这样的列表求值时,先对列表中其它元素先求值,求值的结果作为函数调用的真正参数。然后使用 apply 函数用这些参数调用函数。如果函数是用 lisp 写的,可以理解为把参数和变量绑定到函数后,对函数体顺序求值,返回最后一个 form 的值。

如果第一个元素是一个宏对象,列表里的其它元素不会立即求值,而是根据宏定义进行扩展。如果扩展后还是一个宏调用,则会继续扩展下去,直到扩展的结果不再是一个宏调用为止。例如:

1
2
(defmacro cadr (x)
(list 'car (list 'cdr x)))

这样 (cadr (assq 'handler list)) 扩展后成为 (car (cdr (assq 'handler list)))

第一个元素如果是一个特殊表达式时,它的参数可能并不会全求值。这些特殊表达式通常是用于控制结构或者变量绑定。每个特殊表达式都有对应的求值规则。这在下面会提到。

最后用这个伪代码来说明一下 elisp 中的求值规则:

1
2
3
4
5
6
7
8
9
10
11
12
(defun (eval exp)
(cond
((numberp exp) exp)
((stringp exp) exp)
((arrayp exp) exp)
((symbolp exp) (symbol-value exp))
((special-form-p (car exp))
(eval-special-form exp))
((fboundp (car exp))
(apply (car exp) (cdr exp)))
(t
(error "Unknown expression type -- EVAL %S" exp))))

Comments