【发布时间】:2011-06-25 20:00:53
【问题描述】:
我正在构建一种机制来获取任意 CLOS 对象并从中返回哈希值(对我的调试经验很有用)。
但是,我不确定如何强制变量扩展。我觉得解决方案在于正确使用 gensym,但我不确定如何。
;;helper macro
(defun class-slots-symbols (class-name)
"Returns a list of the symbols used in the class slots"
(mapcar 'closer-mop:slot-definition-name
(closer-mop:class-slots
(find-class class-name))))
;;macro that I am having difficulty with
(defmacro obj-to-hash (obj-inst)
"Reads an object, reflects over its slots, and returns a hash table of them"
`(let ((new-hash (make-hash-table))
(slot-list (class-slots-symbols (type-of ,obj-inst))))
;;The slot-list needs to expand out correctly in the with-slots form
(with-slots (slot-list) obj-inst
(loop for slot in slot-list do ;and also here
(format t "~a~&" slot)
(hashset new-hash (string slot) slot)))))
在macroexpand-1之后,我发现它展开成下面的代码(*bar*是一个类对象):
(macroexpand-1 '(obj-to-hash *bar*))
LET ((NEW-HASH (MAKE-HASH-TABLE))
(SLOT-LIST (CLASS-SLOTS-SYMBOLS (TYPE-OF *BAR*))))
(WITH-SLOTS (SLOT-LIST) ;; <-- this needs to be expanded to *bar*'s slots
*BAR*
(LOOP FOR SLOT IN SLOT-LIST ;;<-- not so important
DO (FORMAT T "~a~&" SLOT) (HASHSET NEW-HASH (STRING SLOT) SLOT))))
显然,问题在于 slot-list 没有被扩展。 (对我而言)不太明显的是解决方案。
跟进:在 Rainer 指出我正确的方向之后:
(defun class-slots-symbols (class-instance)
"Returns a list of the symbols used in the class slots"
(mapcar 'closer-mop:slot-definition-name
(closer-mop:class-slots
(class-of class-instance))))
(defun object-to-hash (obj)
"Reflects over the slots of `obj`, and returns a hash table mapping
slots to their values"
(let ((new-hash (make-hash-table))
(slot-list (class-slots-symbols obj)))
(loop for slot in slot-list do
(hashset new-hash (string slot)
(slot-value obj slot)))
new-hash))
【问题讨论】:
-
为什么是宏?显而易见的解决方案是将其编写为函数。
-
@Rainer:因为 type-of 需要一个符号。将
*bar*作为函数传入会导致*bar*所指的对象被使用,这不是我想要的。 (不过,也许我的想法是错误的) -
符号的类型是符号。无需计算。如果你想要一个对象的类,只需调用函数 CLASS-OF。再想想。将其重写为函数。不需要宏。
标签: macros common-lisp expansion