10.2.4 函数符号转义(DONE)

对于第一个元素为符号的非空列表,Lisp 解释器会先检查该符号的函数cell,然后用该cell的内容去求值。若该cell内容是另一个符号,那么 Lisp 会递归的检查此符号的函数cell,直到查找到一个非符号定义。这个过程称为函数符号转义(symbol function indirection)。查阅 函数名称 章节,以获取更多 函数符号转义 相关的信息。

很显然存在一种可能,即其转义序列是一个无穷的循环,这种情况下,符号的函数cell指回自身。除此之外,我们最终都会得到一个非符号定义,而这个非符号定义应当是函数,或其他合理的对象。

更具体的说,我们最终应当获取一个 Lisp 函数(lambda 表达式) ,或字节码函数,或基础函数,或 Lisp 宏,或 特殊表达式,或自动加载对象。以上列举的美中情况都会在后续章节介绍。如果获得的对象不是上面中列举的对象,Emacs 则会抛出 invalid-function 错误。

下面的例子展示了符号转义的过程。我们首先用 fset 设置符号的 函数cell,再用 symbol-function 来获取该函数 cell 的内容(具体查阅 Function Cells)。再具体一些,我们将符号 car 储存进 first 的函数cell,然后将符号 first 储存进 erste 的函数cell。

;; Build this function cell linkage:
;;   -------------       -----        -------        -------
;;  | #<subr car> | <-- | car |  <-- | first |  <-- | erste |
;;   -------------       -----        -------        -------
(symbol-function 'car)
     ⇒ #<subr car>
(fset 'first 'car)
     ⇒ car
(fset 'erste 'first)
     ⇒ first
(erste '(1 2 3))   ; Call the function referenced by erste.
     ⇒ 1

下面的例子不使用符号转义即可完成函数调用,因为其第一个元素是一个 Lisp 匿名函数,而非一个符号。

((lambda (arg) (erste arg))
 '(1 2 3))
     ⇒ 1

执行一个函数意味着对函数的body部分求值;这个例子里,其body部分使用了ersta的函数符号转义。

这种形式的写法已经很少用了,现在已经被废弃。替代的,你应该这么写:

(funcall (lambda (arg) (erste arg))
         '(1 2 3))

或者直接这么写

(let ((arg '(1 2 3))) (erste arg))

内置函数 indirect-function 提供了一个简单的方式,去显示地指向函数符号转义。

Function: indirect-function function &optional noerror

该函数返回 function 的定义。如果 function 是 一个 符号,那么会继续查找函数的定义,直到找到一个值。如果 function 并不是一个符号,那么返回 function 本身。

如果递归的最终符号没有绑定,那么该符号会返回 nil。如果其符号转义链是循环的,则会抛出 cyclic-function-indirection 错误。

可选参数 noerror 已经废弃,这里是为了保持向后兼容。

这里是一个简略的 indirect-function 定义:

(defun indirect-function (function)
  (if (symbolp function)
      (indirect-function (symbol-function function))
    function))

最后更新于