You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Lisp中如何通过宏遍历类插槽?遇插槽缺失错误求解

问题:如何通过宏遍历类插槽并打印值?

先看正常工作的代码:定义了test-class类和test方法,用print-slot宏可以直接打印指定插槽的值:

(defclass test-class ()
  ((data :initarg :data)
   (other :initarg :other)))

(defmethod test ((tc test-class) &key)
  (macrolet ((print-slot (slot)
               `(with-slots (,slot) tc
                  (format t "slot value: ~a~%" ,slot))))
    (print-slot data)
    (print-slot other)))

调用后输出正常:

CL-USER> (test (make-instance 'test-class :data "data" :other "other"))
slot value: data
slot value: other

但改成用dolist遍历插槽列表调用宏时,会触发SB-PCL::MISSING-SLOT错误:

(defmethod test ((tc test-class) &key)
  (macrolet ((print-slot (slot)
               `(with-slots (,slot) tc
                  (format t "slot value: ~a~%" ,slot))))
    (dolist (slot '(data other))
      (print-slot slot))))

错误信息:

CL-USER> (test (make-instance 'test-class :data "data" :other "other"))
When attempting to read the slot's value (slot-value), the slot
SLOT is missing from the object #<TEST-CLASS {1001E3FFB3}>.
   [Condition of type SB-PCL::MISSING-SLOT]

错误原因

宏是编译时展开的,不是运行时执行的。当你在dolist里写(print-slot slot),宏展开阶段会把slot当成一个插槽名符号,生成的代码是(with-slots (slot) tc ...)——也就是尝试访问对象里名为SLOT的插槽,而不是变量slot的值对应的插槽。你的类里根本没有叫SLOT的插槽,所以报错。

解决方法

方法1:用函数代替宏(推荐,适合运行时动态遍历)

把print-slot改成函数,用slot-value在运行时动态访问插槽,这样就能接收变量形式的插槽名:

(defclass test-class ()
  ((data :initarg :data)
   (other :initarg :other)))

(defmethod test ((tc test-class) &key)
  (flet ((print-slot (slot)
           (format t "slot value: ~a~%" (slot-value tc slot))))
    (dolist (slot '(data other))
      (print-slot slot))))

调用后就能正常遍历打印所有插槽的值。

方法2:用宏生成编译时的遍历代码(适合固定插槽列表)

如果一定要用宏实现,可以定义一个print-slots宏,让它在编译时就把插槽列表展开成多个print-slot调用,这样每个插槽名都是编译时确定的:

(defclass test-class ()
  ((data :initarg :data)
   (other :initarg :other)))

(defmethod test ((tc test-class) &key)
  (macrolet ((print-slot (slot)
               `(with-slots (,slot) tc
                  (format t "slot value: ~a~%" ,slot)))
             (print-slots (&rest slots)
               `(progn
                  ,@(loop for slot in slots
                          collect `(print-slot ,slot)))))
    (print-slots data other)))

这个宏展开后和最初正常的代码完全一样,编译时就处理了每个插槽名,运行时直接访问对应插槽,不会有动态变量的问题。

内容的提问来源于stack exchange,提问作者John Graham

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 04:04:49