Macros 公共Lisp宏:生成列表的正确扩展

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 (

我正在构建一种机制来获取任意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)))))
在宏扩展-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)))))