我想使用宏来创建一个类实例。
我的意思是我想创建一个像:
这样的表达式(make-instance 'message :id id :mid mid)
我这样定义了这个类。
(defclass message ()
((id
:initarg :id
:initform 0
:accessor id)
(mid
:initarg :mid
:initform 0
:accessor mid)))
(defmethod print-object ((obj message) stream)
(print-unreadable-object (obj stream :type t)
(with-slots (id mid) obj
(format stream "~A ~A " id mid))))
和这样的宏。
(defun slotlist (alist)
(mapcan
#'(lambda (x)
(let* ((s (closer-mop:slot-definition-name x))
(k (intern (symbol-name s) :keyword))
(v (assoc k alist)))
(if v (list k (cdr v)))))
(closer-mop:class-direct-slots (find-class 'message))))
(defmacro create-message (alist)
(let ((a (gensym)))
`(let ((,a (slotlist ,alist)))
(make-instance 'message ,@a))))
和json-obj一样:
(setq json-obj'((:id.1)(:mid.2)))
当我应用宏创建消息
时(create-message json-obj)
它扩展如下:
(LET ((#:G1111 (SLOTLIST JSON-ALIT)))
(MAKE-INSTANCE 'MESSAGE . #:G1111))
但实例未正确初始化,因为它显示实例的值为 #
我是否必须拼接map函数字符串并使用apply函数?
答案 0 :(得分:1)
也许我错过了一些关于你的用例的重要内容,但在我看来,你想要的东西很容易被实现为一个函数。
为了创建新的message
实例,我们需要(make-instance 'message :id 1 :mid 2)
之类的内容。所有这些信息都在json-obj
中 - 我们只需要按正确的格式按下它并使用apply
:.
(defun json-attr->arg-list (json-attr)
(list (car json-attr)
(cdr json-attr)))
(defun json-obj->arg-list (json-obj)
(apply #'concatenate 'list
(map 'list #'json-attr->arg-list json-obj)))
(defun create-message (json-obj)
(apply #'make-instance 'message (json-obj->arg-list json-obj)))
现在,我们可以使用之前定义的create-message
:
json-obj
=> (create-message json-obj)
#<MESSAGE 1 2 >
现在,我们可以将其作为宏实现,但重点是什么?我们在运行时之前无法知道json-obj
的值,所以在编译时我们无法做任何事情,除非基本上内联我们刚写的函数。但是(a)在这种情况下不可能以任何可测量的数量增加性能,并且(b)不是宏的用途 - 使用编译器提示进行内嵌。
另外,如果您想使用具有更高阶函数的create-message
,则无论如何都必须将其实现为函数。例如,(map 'list #'create-message list-of-json-objs)
将是一种非常方便的方法,可以从列表中创建message
个对象列表。在common-lisp中,符号可以同时包含宏和与之关联的函数,因此可以为它创建宏和函数绑定。但是,由于你没有从使用宏中获得任何好处,你所要做的就是为自己创造更多的工作。
答案 1 :(得分:0)
目前尚不清楚你要做什么,所以我不知道你是否应该使用apply函数。但是,我可以帮助您解决代码中的一个问题。
在您提供的代码中,您有一个以
开头的准量`(let ((,insym ,json-obj))
然后你从带有
开头的形式的quasiquotation中逃脱,(mapcan #'(lambda (x)
你有一个级别的准引用,一个不引用(逗号)来匹配它。因此,在扩展宏时将评估该表单。但是在同一个不带引号的形式中,你试图取消引用另一种形式:
,insym
这是一个错误,因为您已从单级准规则中逃脱。要修复此错误(尽管可能不是整个宏),您需要删除最后一个逗号。
本文可能有助于学习有关quasquotation的更多细节:http://repository.readscheme.org/ftp/papers/pepm99/bawden.pdf
答案 2 :(得分:0)
根本问题在于,您在准报价列表中引用的a
是您在外部let
中绑定的那个,并且它会扩展为一个gensym。
让我举一个小例子:
* (let ((b 'a)) `(let ((,b (foo))) (print ,@b)))
(LET ((A (FOO)))
(PRINT . A))
正如我们所看到的,b
绑定到a
,这不是列表,所以拼接它,我们最终得到一个虚线对。
如果我们跳过扩展它,我们就越接近正确。
所以,你可以尝试以下方法之一,而不是你拥有的:
(defmacro create-message (alist)
(let ((a (gensym)))
`(let ((,a (slotlist ,alist)))
(make-instance 'message ',a))))
或:
(defmacro create-message (alist)
`(make-instance 'message (slotlist ,alist))) ; You may want ',alist
答案 3 :(得分:0)
以下是使用cl-json
:
(ql:quickload "cl-json")
(defclass message ()
((id :initarg :id :initform 0 :accessor id)
(mid :initarg :mid :initform 0 :accessor mid)))
(cl-json:encode-json (make-instance 'message :mid 42 :id 13))
{"id":13,"mid":42}
真正的全部内容都是如此。您可能需要在此处阅读更多内容:http://common-lisp.net/project/cl-json/#STREAMING-API(与API一样,文档有点繁忙,但仍然可以节省您从头开始编写文档的时间。)
暂时不说:JSON并不是那么好,因为它经常被广告宣传。如果您可以自由选择格式,请查看Protobuf或Trift(它们也很糟糕,但没有JSON那么糟糕):)
答案 4 :(得分:0)
问题是宏不知道参数的值,所以它将被视为符号。因此,在宏的主体中,它将替换符号,而不是符号的值。
,@(slotlist ,alist)
是错误,因为在体内,@不能包括,要获取符号值。 如果你使用@(slotlist alist),那么符号alist将永远无法正确评估,宏将其视为一个符号。
解决问题的正确方法是使用eval函数:
(defmacro create-message (alist)
`(make-instance 'message ,@(slotlist (eval alist))))