使用结构作为属性列表来宏

时间:2021-01-10 02:34:47

标签: common-lisp

我有一个带有 :name:value 的结构,我想将其用作宏的参数。但我不知道如何告诉 lisp。

我可以像这样写出电话

(sxql:yield (sxql:set= :name "a" :value 1))
<块引用>

"SET 名称 = ?, 值 = ?"

("a" 1)

但我想使用已经存在的结构

(defstruct my-struct name value)
(setq x (make-my-struct :name "a" :value 1))
; #S(MY-STRUCT :NAME "a" :VALUE 1)

使用来自Common LISP: convert (unknown) struct object to plist?的回答 我做了

(defun struct-plist (x)
  "make struct X into a property list. ugly kludge"
  (let* ((slots (sb-mop:class-slots (class-of x)))
     (names (mapcar 'sb-mop:slot-definition-name slots)))
    (alexandria:flatten
     (mapcar (lambda (n) (list (intern (string n) "KEYWORD")
                   (slot-value x n)))
         names))))
(setq p (struct-plist x)) ; (:NAME "a" :VALUE 1)

我幼稚的尝试是

(sxql:set= p) ; error in FORMAT: No more argument SET ~{~A = ~A~^, ~}
(funcall 'sxql:set= p) ; SXQL:SET= is a macro, not a function.
(macroexpand (sxql:set= p)) ; error in FORMAT ...

我想这是一个简单/基本的 lisp 编程问题。但我不确定如何提问(或寻找答案)。我也希望有一个比我迄今为止偶然发现的更好的 struct<->plist 故事。

编辑:如果这真的是一个 xy 问题。我已经使用 flydata:defmodel 创建了结构,并且我想使用相同的模型插入到数据库中。

1 个答案:

答案 0 :(得分:1)

这绝对是一个 xy 问题:不幸的是,我对 y(flydata?)的理解不够好,无法回答 y 部分。

这就是为什么您尝试执行的操作无效的原因。考虑正在编译的文件中的这段代码:

(defstruct mine name value)

...

(sxql:set= <anything derived from mine>)

编译这个文件必须满足两个约束:

  1. 没有完全创建结构类型mine(参见defstruct);
  2. 它必须宏展开 sxql:set=

这些约束意味着sxql:set= 在扩展时无法了解结构。因此,任何依赖于结构信息的技巧都必须在编译时提供这些信息。

正如我所说,我对 y 部分的理解不够好,无法理解您要做什么,但对此的一种黑客方法是:

  • defstruct 编写一个包装器,在编译时存储信息(严格来说:在宏扩展时);
  • sxql:set= 编写一个包装器,使用该信息扩展为有意义的内容。

这是 defstruct 的无意识包装。请注意,这是无脑:它只能理解最简单的 defstruct 形式,即使这样也可能是错误的。它仅作为示例存在。

(eval-when (:compile-toplevel :load-toplevel :execute)
  (defvar *structure-information* '()))
  
(defmacro define-mindless-structure (name &body slots)
  (assert (and (symbolp name)
               (every #'symbolp slots))
      (name slots)
    "I am too mindless")
  (let ((found (or (assoc name *structure-information*)
                   (car (push (list name) *structure-information*)))))
    (setf (cdr found) (mapcar (lambda (slot)
                                (list slot (intern (symbol-name slot)
                                                   (find-package "KEYWORD"))
                                      (intern (concatenate 'string
                                                           (symbol-name name)
                                                           "-"
                                                           (symbol-name slot)))))
                              slots)))
  `(defstruct ,name ,@slots))

那么现在

(define-mindless-structure mine
  name value)

将扩展到 (defstruct mine name value) 并且,在宏扩展时将在 *structure-information* 中存储有关此结构的一些信息。

现在我不再真正理解您需要做什么,因为我不知道 sxql:set= 的意思,但它可能是这样的:

(defmacro mindless-set= ((s o))
  (let ((info (assoc s *structure-information*))
        (ov (make-symbol "O")))
    (unless info
      (error "no information for ~A" s))
    `(let ((,ov ,o))
       (sxql:set= ,@(loop for (slot initarg accessor) in (cdr info)
                          ;; the compiler will whine about slot annoyingly
                          collect initarg
                          collect `(,accessor ,ov))))))

所以对于这个宏,假设在宏展开时已经看到了一个合适的 define-mindless-structure 形式的 mine,那么

(mindless-set= (mine it))

将扩展为

(let ((#:o it))
  (set= :name (mine-name #:o) :value (mine-value #:o)))

但是,正如我所说,我不确定您真正想要的扩展是什么。


最后,在考虑使用上述任何东西之前,有必要四处看看是否有提供这样的编译/宏扩展时功能的可移植性库:很有可能是这样的,就像我不一样'不跟上事情。

相关问题