这是一个很好的例子,它很好地使用了一个双层方法,一个明确的基于函数的图层,然后是一个更漂亮的宏层。
请注意以下内容假定为Common Lisp:从您的问题中看,您可能会问到elisp,在这种情况下,可以使用这种方式来工作,但这更加痛苦。
首先,我们将继续回调名为*callbacks*
的ALIST:
(defvar *callbacks* '())
这里是它清除回调
(defun initialize-callbacks()
(setf *callbacks* '())
(values)
这里的ALIST功能是用于安装一个回调函数。它通过搜索列表来查看是否存在具有给定名称的回调,以及是否有替换它,否则安装新的。像功能层中的所有功能一样,我们可以指定测试函数,让我们知道两个回调名称是否相同:默认情况下,这是#'eql
,它适用于符号和数字,但不适用于字符串。对于回调的名称,符号可能是比字符串更好的选择,但我们将在下面处理。
(defun install-callback (name function &key (test #'eql))
(let ((found (assoc name *callbacks* :test test)))
(if found
(setf (cdr found) function)
(push (cons name function) *callbacks*)))
name)
这里是一个函数来找到一个回调,返回函数对象,或者nil
如果没有与该名称的回调。
(defun find-callback (name &key (test #'eql))
(cdr (assoc name *callbacks* :test test)))
还有一个函数来删除一个命名的回调函数。这并不能告诉你它是否做了任何事情:或许它应该。
(defun remove-callback (name &key (test #'eql))
(setf *callbacks* (delete name *callbacks* :key #'car :test test))
name)
现在来宏观层。这个语法将会是(define-callback name arguments ...)
,所以它看起来有点像函数定义。
关于这个宏有三件事要知道。
这是一个有点聪明:因为你可以知道宏扩展时什么样的事情回调的名称,你可以决定,然后有安装回调的时候使用什么考验,它做这个。如果名称是一个符号,它也会将符号命名的block
包装在函数定义的主体周围,因此它更像是由defun
定义的函数:特别是您可以在主体中使用return-from
。如果名称不是符号,则不这样做。
它不够聪明:特别是它没有以任何有用的方式处理docstrings(它应该将它们从我认为的块中拉出)。我不确定这个问题。
决定测试使用等'#'eql
表达式其内容(quote (function eql))
开关:其是为了避免在功能布线到膨胀因为功能在CL externalisable对象。不过我不确定我是否有这个权利:我认为安全是什么,但它可能并不需要。
所以,在这里它是
(defmacro define-callback (name arguments &body body)
`(install-callback ',name
,(if (symbolp name)
`(lambda ,arguments
(block ,name
,@body))
`(lambda ,arguments
,@body))
:test ,(typecase name
(string '#'string=)
(symbol '#'eql)
(number '#'=)
(t '#'equal))))
最后这里有被定义两个不同的回调:
(define-callback "foo" (x)
(+ x 3))
(define-callback foo (x)
(return-from foo (+ x 1)))
来源
2017-02-12 14:17:27
tfb
数据中的引用符号名单/ conses没有多大意义。 '(foo'bar)'< - 为什么要引用'bar'?如果列表是数据,则可能需要引用列表,但不包括内容,以防止评估。 –
是的,报价符号应该在整个列表之外;尽管如此,该列表实际上并不会出现在源代码中。 –