我在Common Lisp中有一个课程:
(defclass my-cool-class()
((variable1
:initarg :variable1
:accessor variable1
:initform (error "Must supply value to variable1"))
(variable2
:initarg :variable2
:accessor variable2
:initform (error "Must supply value to variable2"))
我想创建一个可以简化输入
冗余的宏(defmacro make-slot (slot-name)
`(slot-name
:initarg :,slot-name
:accessor :,slot-name
:initform (error "Must supply value")))
最终我想(defclass my-cool-class()(make-slots'(foo bar baz))并自动将foo,bar和baz作为插槽。
但是,当我去做macroexpand-1
制作插槽时,男孩我怎么会收到读者错误。
第一个是“结肠后非法终止性格......”然后继续前进。
SBCL 1.0.37。
编辑:示例在系统上语法正确,我在复制之前做了一些编辑。
六个月后 -
(defun build-var (classname var)
(list var
:initform nil
:accessor (intern (concatenate 'string (string classname) "-"
(string var)))
:initarg (intern (string var) :keyword)))
(defun build-varlist (classname varlist)
(loop for var in varlist
collect (build-var classname var)))
(defmacro defobject (name &rest varlist)
"Defines a class with a set of behavior.
Variables are accessed by name-varname.
(defobject classname v1 v2 v3)
"
`(defclass ,name ()
,(build-varlist name varlist))):
两年半之后。
我在其他地方发现了6个月大的代码。虽然我受宠若惊,但它也提醒我更新这个。
如果您喜欢这个想法,我会将此代码保留在:https://github.com/pnathan/defobject。和以前一样,它的目标是以最少的重复输入生成CLOS类。存在类似的系统,称为DEFCLASS-STAR。建议有兴趣的人士查看两者。
答案 0 :(得分:5)
您不能将宏放在您想要的代码中。在CLHS中阅读构造的语法。
例如你不能这样做:
(defun foo (make-arg-list 'a 'b) a b)
DEFUN需要一个arglist,而不是一个创建arglist的函数。
Lisp扩展了宏,预计会出现Lisp格式。在预期其他列表(例如插槽列表)的地方,Lisp不会宏扩展。
类似的DEFCLASS需要一个插槽列表,而不是一个创建插槽列表的函数。 类似于插槽列表,DEFCLASS希望每个插槽都是名称或描述插槽的列表。
参见DEFCLASS的语法: http://www.lispworks.com/documentation/HyperSpec/Body/m_defcla.htm
你不能把逗号放在你想要的地方。
Probabaly一本基本的Lisp书可能有所帮助。阅读Lisp语法。
:,foo
以上没有意义。
逗号运算符将项放入反引号列表中。它不会将项目放入符号中。
如果要创建符号,则需要调用INTERN或MAKE-SYMBOL。
<强>解决方案强>
编写一个MY-DEFCLASS宏,允许更短的语法并扩展到DEFCLASS。已有DEFCLASS *宏在库中执行类似的操作。
答案 1 :(得分:3)
我通常使用这样的东西
(defmacro mydefclass (name fields)
`(defclass ,name ()
,(let ((res nil))
(dolist (f fields)
(let* ((fname (symbol-name f))
(kw (intern fname :keyword)))
(push `(,f :accessor ,kw
:initarg ,kw
:initform (error
,(format NIL "Must supply value to ~a"
fname)))
res)))
(nreverse res))))
然后
(mydefclass foo (x y z))
添加一些逻辑来处理对自定义插槽的需求非常容易(例如,当字段是列表而不是符号时,您可以在扩展中逐字复制输入)
答案 2 :(得分:2)
正如Rainer所说,只有在函数调用可以接受的情况下才会扩展宏。
我在限定插槽时需要实际打字的锅炉板的做法是,有两个编辑器宏,一个用于带读卡器的插槽,另一个用于带有存取器的插槽(我很少有单独的插槽作家,但如果我这样做,我必须手工编写)。
答案 3 :(得分:0)
宏从上到下递归扩展。在您的示例中,defclass
宏首先展开 - 在您的make-slot
宏之前。扩展defclass
的代码并不期望未扩展的make-slot
宏 - 它期望一个插槽定义。
正如其他人所建议的那样,读者错误是因为`:,symbol
无效的Lisp。但是,首先将关键字传递给宏很容易。