UP | HOME

求值规则

Table of Contents

至此,elisp 中最常见的数据类型已经介绍完了。可以真正开始学习怎样写一个 elisp 程序

如果想深入了解一下 lisp 是如何工作的,不妨先花些时间看看 lisp 的求值过程

当然忽略这一部分也是可以的,因为我觉得这个求值规则是那么自然,以至于你会认为它就是应该这样的

求值是 lisp 解释器的核心,理解了求值过程也就学会了 lisp 编程的一半

一个要求值的 lisp 对象被称为 表达式 (form)。所有的表达式可以分为三种

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

下面实例显示 indirection function:

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

函数调用

对于第一个元素是 lisp 函数对象byte-code 对象原子函数 时,这个列表也称为 函数调用 (funtion call)。对这样的列表求值时:

  1. 列表中 其它元素先求值 ,求值的结果作为函数调用的真正参数
  2. 使用 apply 函数用这些参数调用函数
如果函数是用 lisp 写的,可以理解为把参数和变量绑定到函数后,对函数体顺序求值,返回最后一个 form 的值

宏调用

如果第一个元素是一个 宏对象

  1. 列表里的 其它元素不会立即求值 ,而是根据 宏定义进行扩展
  2. 如果扩展后还是一个宏调用,则会继续扩展下去,直到扩展的结果不再是一个宏调用为止
(defmacro my-cadr (x)
  (list 'car (list 'cdr x)))
这样 (my-cadr (assq 'handler list)) 扩展后成为 (car (cdr (assq 'handler list))) 

特殊表达式

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

define,cond 等被认为是特殊表达式

注意:if, while, dotimes等却经常是用cond实现的宏

evaluator

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

(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))))

Next:变量

Previous:符号

Home:目录