Common Lisp宏:正确扩展生成的列表

时间:2011-06-25 20:00:53

标签: 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
      (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))

1 个答案:

答案 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)))))

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