我正在构建一个机制来获取任意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))))
显然,问题是插槽列表没有被扩展。对我来说不太明显的是解决方案。
跟进: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))
答案 0 :(得分:7)
只是看着它,我看不出为什么这应该是一个宏。将其重写为函数将为您节省很多麻烦。
使用WITH-SLOTS是不可能的。在运行时之前,通常不知道该对象。编译器需要在编译时知道对象的槽。您需要使用SLOT-VALUE并在运行时查找插槽值。
你在想很多方面太复杂了,你的代码有点混乱。你可以通过遵循简单的规则并避免一些措辞来消除一些混乱。
让我们看看你的代码:
首先,它不是辅助宏,因为后面是一个函数。
;;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)
措词不好:obj-inst。将其命名为对象或实例。不是两个。
"Reads an object, reflects over its slots, and returns a hash table of them"
文档很差:你什么都不读。 Read是一个I / O操作,代码中没有。你在谈论一个'对象',但在你上面你有'obj-inst'之类的东西。为什么用两种不同的方式谈论同样的事情?您可能想要记录哈希表实际映射的内容。从哪个键到哪个值?
`(let ((new-hash (make-hash-table))
new-hash也是一个糟糕的名字。基本上就是哈希表。
(slot-list (class-slots-symbols (type-of ,obj-inst))))
为什么TYPE-OF然后在辅助函数中调用FIND-CLASS? Common Lisp有CLASS-OF,它直接返回类。
;;The slot-list needs to expand out correctly in the with-slots form
(with-slots (slot-list) obj-inst
上面不起作用,因为WITH-SLOTS在编译时需要插槽名称,而不是插槽列表。
(loop for slot in slot-list do ;and also here
(format t "~a~&" slot)
(hashset new-hash (string slot) slot)
不需要HASHSET,除非它有特殊之处。设置值的常用方法是通过SETF。 SETF采用表单来读取一个地方和表单来计算一个值。就这样。它适用于各种数据结构。人们永远不需要再次记住作者函数的外观(名称,参数列表......)。
))))
这是我的版本:
请注意,我使用的是CLOS包,您可能希望使用包CLOSER-MOP
(defun class-slots-symbols (class)
"Returns a list of the symbol names of the class slots"
(mapcar 'clos:slot-definition-name
(clos:class-slots class)))
上面是一个简单的函数,它接受一个类并返回插槽名称列表。
接下来,我们有一个简单的函数,在这种形式下已经在Common Lisp中写了一百万次:
(defun object-to-hash (object)
"returns a hashtable with the object's slots as keys and slot-values as values"
(let ((hash-table (make-hash-table)))
(loop for slot-name in (class-slots-symbols (class-of object))
do (setf (gethash slot-name hash-table)
(string (slot-value object slot-name))))
hash-table))
我们也可以将它重写为稍微旧式的Lisp:
(defun object-to-hash (object &aux (hash-table (make-hash-table)))
"returns a hashtable with the object's slots as keys
and string versions of the slot-values as values"
(dolist (slot-name (class-slots-symbols (class-of object)) hash-table)
(setf (gethash slot-name hash-table)
(string (slot-value object slot-name)))))
上面的内容要简单得多,并且完全混淆了宏,生成代码,编译时间信息与运行时,...已删除。理解,维护和调试要容易得多。