2017-02-12 147 views
0

我想使回调函数列表,这可能是这样的:注册列表中的一个功能被定义它

(("command1" . 'callback1) 
("command2" . 'callback2) 
    etc) 

我想,如果我能可以做类似:

(define-callback callback1 "command1" args 
    (whatever the function does)) 

不是

(defun callback1 (args) 
    (whatever the function does)) 

(add-to-list 'callback-info ("command1" . 'callback1)) 

是否有这样做的一个简便的方法,例如,与宏?

+0

数据中的引用符号名单/ conses没有多大意义。 '(foo'bar)'< - 为什么要引用'bar'?如果列表是数据,则可能需要引用列表,但不包括内容,以防止评估。 –

+0

是的,报价符号应该在整个列表之外;尽管如此,该列表实际上并不会出现在源代码中。 –

回答

1

这些列表在Lisp中被称为相关列表

CL-USER 120 > (defvar *foo* '(("c1" . c1) ("c2" . c2))) 
*FOO* 

CL-USER 121 > (setf *foo* (acons "c0" `c1 *foo*)) 
(("c0" . C1) ("c1" . C1) ("c2" . C2)) 

CL-USER 122 > (assoc "c1" *foo* :test #'equal) 
("c1" . C1) 

您可以为此编写宏,但为什么?宏是高级的Lisp,你可能想首先得到基本的东西。

的一些问题与大家例如,您可能想看看:

  • 什么assoc命令列表?
  • 关联列表中有用的键类型是什么?
  • 为什么你不需要在数据引用符号列出
  • 变量中没有报价
  • 数据列表需要括

你可以一样容易创建回调这类名单没有宏。我们可以想像一个功能create-callback,这将是这样使用:

(create-callback 'callback1 "command1" 
    (lambda (arg) 
    (whatever the function does))) 

现在,你为什么要使用宏,而不是一个简单的功能吗?

2

这是一个很好的例子,它很好地使用了一个双层方法,一个明确的基于函数的图层,然后是一个更漂亮的宏层。

请注意以下内容假定为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 ...),所以它看起来有点像函数定义。

关于这个宏有三件事要知道。

  1. 这是一个有点聪明:因为你可以知道宏扩展时什么样的事情回调的名称,你可以决定,然后有安装回调的时候使用什么考验,它做这个。如果名称是一个符号,它也会将符号命名的block包装在函数定义的主体周围,因此它更像是由defun定义的函数:特别是您可以在主体中使用return-from。如果名称不是符号,则不这样做。

  2. 它不够聪明:特别是它没有以任何有用的方式处理docstrings(它应该将它们从我认为的块中拉出)。我不确定这个问题。

  3. 决定测试使用等'#'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))) 
+0

非常感谢 - 我知道双层方法是最好的。最后,我回到了这样的: (我不能解决如何获得反馈到评论,见下文) –

0

最后,由应答以上的协助下,我得到了下来类似于:

(defmacro mk-make-command (name &rest body) 
    (let ((func-sym (intern (format "mk-cmd-%s" name)))) 
    (mk-register-command name func-sym) 
    `(defun ,func-sym (args &rest rest) 
     (progn 
     ,@body)))) 
相关问题