0

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

1 回答 1

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"

糟糕的文档:你什么都不读。读取是 I/O 操作,在您的代码中没有。您正在谈论一个“对象”,但在上面您有类似“obj-inst”的东西。为什么用两种不同的方式谈论同一件事?您可能想要记录哈希表实际映射的内容。从哪些键到哪些值?

  `(let ((new-hash (make-hash-table))

new-hash 也是个蹩脚的名字。基本上事情是一个哈希表。

    (slot-list (class-slots-symbols (type-of ,obj-inst))))

为什么 TYPE-OF 然后在 helper 函数中调用 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)))))

上面要简单得多,并且对宏、生成代码、编译时间信息与运行时等方面的全部混淆,......已删除。它更容易理解、维护和调试。

于 2011-06-25T20:14:28.707 回答