类访问器的宏

时间:2018-03-19 19:03:30

标签: lisp common-lisp

我正在尝试编写一个宏,它会自动为所有插槽写一个类的加速器。我想出了这个

(defmacro defacc (class)
  (loop for name in (mapcar #'slot-definition-name
                            (class-slots (class-of (make-instance `,class))))
       do
       `(defun ,name (,class)
          (slot-value ,class ',name))))

但是它没有定义任何函数(没有循环,它适用于单个函数和单个插槽名称。我不知道问题所在。我还尝试了(class-of (make-instance 'class))(class-of ',class)。没有功能出现。

P.S。我没有忘记用我的班级写这个宏。

3 个答案:

答案 0 :(得分:5)

定义个别功能

我们假设您有两种形式定义函数foobar

CL-USER 68 > (defun foo (baz) baz)
FOO

CL-USER 69 > (defun bar (baz) baz)
BAR

以一种Lisp形式定义更多功能

现在您想在一个Lisp表单中使用这两个表单。它们的典型方式是使用一个提供表格体的运算符。

progn就是这样的运营商。其子表单将逐个执行,并返回最后的结果。

CL-USER 70 > (progn
               (defun foo (baz) baz)
               (defun bar (baz) baz))
BAR

生成PROGN表格

现在假设你有Lisp代码作为数据,这里是一个Lisp表单列表。

CL-USER 71 > '((defun foo (baz) baz)
               (defun bar (baz) baz))
((DEFUN FOO (BAZ) BAZ)
 (DEFUN BAR (BAZ) BAZ))

要创建有效的progn表单,您只需将progn放在列表的前面:

CL-USER 72 > (cons 'progn
                   '((defun foo (baz) baz)
                     (defun bar (baz) baz)))
(PROGN
 (DEFUN FOO (BAZ) BAZ)
 (DEFUN BAR (BAZ) BAZ))

生成PROGN表单的宏

宏需要生成的内容:有效的progn表单。

生成progn表单的宏示例:

CL-USER 74 > (defmacro baz (sym)
               (cons 'progn
                     `((defun foo (,sym) ,sym)
                       (defun bar (,sym) ,sym))))
BAZ

CL-USER 75 > (pprint (macroexpand-1 '(baz fourtytwo)))

(PROGN
  (DEFUN FOO (FOURTYTWO) FOURTYTWO)
  (DEFUN BAR (FOURTYTWO) FOURTYTWO))

答案 1 :(得分:4)

Defclass已经有了用于定义访问者,读者和作者的插槽选项:

(defclass foo ()
  ((bar :accessor foo-bar)))

这与类一起定义了一个专门用于foo-bar的方法foo,它返回bar个插槽的值,以及一个方法setf foo-bar来编写到那个位置。还有一个:reader和一个:writer选项可以单独定义它们。

很多人都围绕defclass编写了自己的包装器,以使其更加方便(例如,自动使用命名约定)。然而,缺点是本地使用的包装器很可能对其他人不熟悉,并且增加的认知负荷通常不被视为值得进行轻微的便利性改进。

答案 2 :(得分:3)

假设你有类定义,你想添加自动插槽读取器,那么简单的方法是使用自定义的defclass宏,例如: (defmacro defclass / auto-reader(class supers                                       槽定义                                       & rest class-options)   ;;这与DEFCLASS完全相同,只不过它会自动添加一个   ;;每个插槽的读取器方法,以插槽命名。   `(defclass,class,supers      ,(mapcar#'(lambda(slot-definition)                   ;;根据需要重写插槽定义                   (etypecase插槽定义                     ;;插槽定义可以是......                     (符号                      ;; ...命名槽的符号,在这种情况下我们                      ;;需要用a创建一个列表槽定义                      ;;读者指定......                      `(,插槽定义:阅读器,插槽定义))                     (名单                      ;; ...或列表,其第一个元素是名称                      ;;槽和其余元素                      ;;定义各种选项。在这种情况下我们只是                      ;;附加读者方法的规范                      ;;我们需要列表(这可能是略有的                      ;;脆弱:我不知道如果你结束会发生什么                      ;;喜欢的东西                      ;; (x ...:读者x ...:读者x)                      ;;但这在实践中不太可能是一个问题)。                      (追加插槽定义                              `(:读者,(第一个插槽定义))))))               插槽)      @类的选项)) 这将自动定义读者,并且还将使这些读者可靠地工作(例如,如果您有两个定义相同插槽名称的类,它们将起作用)。 请注意,我不清楚单独的defacc宏是否可以工作。特别考虑尝试编译包含以下内容的文件: (defclass foo(...)   (s1 s2 ......)) ... (defacc foo ......) 这里defacc需要能够在编译时找到foo的插槽,我相当确定它不能可靠地执行此操作:规范告诉您defclass需要提供有关类可用的一些信息在编译时(例如它是一个类)但我完全不确定它是否需要像插槽定义那样可用。但是,MOP在任何情况下都不在规范范围内,可能是MOPpy实现(我猜所有这些)确实可以提供这些信息。