如何解决使用结构作为属性列表来宏
我有一个带有 :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 编程问题。但我不确定如何提问(或寻找答案)。我也希望有一个比我迄今为止偶然发现的更好的 structplist 故事。
编辑:如果这真的是一个 xy 问题。我已经使用 flydata:defmodel
创建了结构,并且我想使用相同的模型插入到数据库中。
解决方法
这绝对是一个 xy 问题:不幸的是,我对 y(flydata?)的理解不够好,无法回答 y 部分。
这就是为什么您尝试执行的操作无效的原因。考虑正在编译的文件中的这段代码:
(defstruct mine name value)
...
(sxql:set= <anything derived from mine>)
编译这个文件必须满足两个约束:
- 它没有完全创建结构类型
mine
(参见defstruct); - 它必须宏展开
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)))
但是,正如我所说,我不确定您真正想要的扩展是什么。
最后,在考虑使用上述任何东西之前,有必要四处看看是否有提供这样的编译/宏扩展时功能的可移植性库:很有可能是这样的,就像我不一样'不跟上事情。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。