在宏中映射用常见的lisp替换错误

时间:2013-11-15 01:55:58

标签: macros common-lisp

我想使用宏来创建一个类实例。

我的意思是我想创建一个像:

这样的表达式
(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函数?

5 个答案:

答案 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))))