Macros 公共Lisp宏:生成列表的正确扩展
我正在构建一种机制来获取任意CLOS对象并从中返回哈希(在我的调试经验中很有用) 然而,我不知道如何强制变量展开。我觉得解决办法在于正确使用gensym,但我不确定如何使用Macros 公共Lisp宏:生成列表的正确扩展,macros,common-lisp,expansion,Macros,Common Lisp,Expansion,我正在构建一种机制来获取任意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 (
;;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)))))
在宏扩展-1之后,我发现它扩展为以下代码(*bar*
是类对象):
只是看看它,我看不出为什么这应该是一个宏。将其重写为函数将为您节省很多麻烦 使用WITH-SLOT是不可能的,因为它们是您尝试的方式。在运行时之前,通常不知道该对象。编译器需要在编译时就知道对象的插槽。您需要使用SLOT-VALUE并在运行时查找SLOT值 您在许多方面的思维过于复杂,代码也有点混乱。你可以通过遵循简单的规则和避免一些措辞来消除一些混乱 让我们看看您的代码: 首先,它不是一个helper宏,因为下面是一个函数
;;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。要么将其命名为object,要么命名为instance。不是两者都有
"Reads an object, reflects over its slots, and returns a hash table of them"
糟糕的文档:你什么都不读。Read是一个I/O操作,在代码中为none。你说的是一个“对象”,但上面有一个类似“obj inst”的东西。为什么用两种不同的方式谈论同一件事?您可能需要记录哈希表实际映射的内容。从哪些键到哪些值
`(let ((new-hash (make-hash-table))
新哈希也是一个糟糕的名字。基本上就是一个哈希表
(slot-list (class-slots-symbols (type-of ,obj-inst))))
为什么要在助手函数中调用FIND-CLASS?Common Lisp具有类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采用读取一个位置的形式和计算一个值的形式。这就是全部。它适用于所有类型的数据结构。您永远不需要再记住writer函数的外观(名称、参数列表等等)
这是我的版本:
请注意,我使用的是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)))))
上面的方法简单得多,而且完全混淆了宏、生成代码、编译时信息和运行时。。。远离的。它更易于理解、维护和调试。为什么它是宏?最明显的解决办法是把它写成一个函数。@Rainer:因为of的类型带有一个符号。将
*bar*
作为函数传入会导致使用*bar*
引用的引用对象,这不是我想要的。(然而,也许我的想法是错误的)符号的类型是符号。不需要计算。如果需要对象的类,只需调用函数class-of。再想想。将其作为函数重写。不需要宏。插槽值完成了它。谢谢非常感谢。:-)接下来:我使用插槽是因为我不知道插槽的价值。With slots需要使用扩展的插槽列表,因此需要使用宏。使用类也稍微简化了。我的最终解决方案张贴在上面:看起来很像你的。@Paul Nathan:“因此使用了宏”-但这不起作用。在运行之前,您无法知道对象的插槽。编译器根本不知道它们。使用宏无法解决此问题。
(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)))
(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))
(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)))))