首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用宏上的循环生成公共Lisp中的类槽

使用宏上的循环生成公共Lisp中的类槽
EN

Stack Overflow用户
提问于 2015-08-29 02:00:45
回答 1查看 541关注 0票数 4

在CLOS上上课时,我多次遇到相同的模式:

代码语言:javascript
复制
(defclass class-name () 
  ((field-1
      :initarg field-1
      :initform some-value
      :accessor field-1)
   (field-2
      :initarg field-2
      :initform another-value
      :accessor field-2)
   (...)
   (field-n
      :initarg field-n
      :initform n-value
      :accessor field-n)))

(随着时间的推移,这是否是好的设计,我会学到的)

我试图用宏来处理这个问题,这样我就可以调用,例如:

代码语言:javascript
复制
(defclass-with-accessors 'class-name
   (('field-1 some-value)
    ('field-2 another-value)
    (...)
    ('field-n n-value)))

我的第一个处理(目前忽略卫生)是分成两个宏:一个创建每个字段,另一个创建类本身。

生成访问器字段的宏似乎是正确的:

代码语言:javascript
复制
(defmacro make-accessor-field (name form)
  `(,name
   :initarg ,(make-keyword name)
   :initform ,form
   :accessor ,name))

但我没能搞清楚主要的宏观。我的第一次尝试是:

代码语言:javascript
复制
(defmacro defclass-with-accessors (name body)
  `(defclass ,name () \(
     ,(loop for my-slot in body collect
       (make-accessor-field (car my-slot) (cadr my-slot)))))

但这是无效的,SBCL在Def宏计算中给出了以下错误:

代码语言:javascript
复制
; in: DEFMACRO DEFCLASS-WITH-ACCESSORS
;     (MAKE-ACCESSOR-FIELD (CAR MY-SLOT) (CADR MY-SLOT))
; 
; caught ERROR:
;   during macroexpansion of (MAKE-ACCESSOR-FIELD (CAR MY-SLOT) (CADR MY-SLOT)).
;   Use *BREAK-ON-SIGNALS* to intercept.
;   
;    The value (CAR MY-SLOT)
;    is not of type
;      (OR (VECTOR CHARACTER) (VECTOR NIL) BASE-STRING SYMBOL CHARACTER).
; 
; compilation unit finished
;   caught 1 ERROR condition
STYLE-WARNING:
   redefining COMMON-LISP-USER::DEFCLASS-WITH-ACCESSORS in DEFMACRO

到底发生了什么?当没有定义插槽时,编译器如何分辨(汽车插槽)的类型?如何继续正确定义此宏?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-08-29 07:01:13

基本错误

这个宏是错误的,因为它不应该是一个宏:

代码语言:javascript
复制
(defmacro make-accessor-field (name form)
  `(,name
   :initarg ,(make-keyword name)
   :initform ,form
   :accessor ,name))

宏窗体应展开为代码。此宏将窗体展开为用于槽描述的列表。插槽描述不是代码,而是defclass插槽列表的一部分。因此,您不能使用这样的宏,因为返回的值应该是代码,而不是槽描述列表。

另外,通常不会在宏名中使用MAKE-。那更像是一种惯例。MAKE-SOMETHING应该是一个函数。每当您做某事时,都会在运行时创建一些东西,因此它应该是一个函数。有时,人们也希望将make应用于事物列表,然后再一次,函数是首选的。

这也是错误的,因为有一个以括号作为其名称的符号:

代码语言:javascript
复制
(defmacro defclass-with-accessors (name body)
  `(defclass ,name () \(    ;  <-  what is this?
     ,(loop for my-slot in body collect
       (make-accessor-field (car my-slot) (cadr my-slot)))))

这段代码也不是一个好主意,因为引号没有用:

代码语言:javascript
复制
(defclass-with-accessors 'class-name
   (('field-1 some-value)
    ('field-2 another-value)
    (...)
    ('field-n n-value)))

如果您查看defclass,它不需要引用名称。因此,在您的defclass变体中,也不应该有引号。

,让我们尝试改进它,

来自某个虚代码库的示例表单:

代码语言:javascript
复制
(defclass-with-accessors foo
   ((bar 10)
    (baz (sin pi)))

MAKE-ACCESSOR-FIELD现在是一个函数:

代码语言:javascript
复制
(defun make-accessor-field (name form)
  `(,name
    :initarg  ,(intern (symbol-name name) "KEYWORD")
    :initform ,form
    :accessor ,name))

新的DEFCLASS-WITH-ACCESSORS

代码语言:javascript
复制
(defmacro defclass-with-accessors (name slot-descriptions)
  `(defclass ,name ()
     ,(loop for (slot-name form) in slot-descriptions
            collect (make-accessor-field slot-name form))))

,让我们检查一下扩展:

macroexpand-1在toplevel展开表单一次,pprint以某种自动格式化的方式打印s-表达式:

代码语言:javascript
复制
CL-USER 12 > (pprint (macroexpand-1 '(defclass-with-accessors foo
                                         ((bar 10)
                                          (baz (sin pi))))))

(DEFCLASS FOO
          NIL
          ((BAR :INITARG :BAR :INITFORM 10 :ACCESSOR BAR)
           (BAZ :INITARG :BAZ :INITFORM (SIN PI) :ACCESSOR BAZ)))

看上去没问题。

票数 13
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/32281981

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档