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

如何访问类中的槽?能否在类定义中像let*那样用一个槽定义另一个?

关于Common Lisp类槽的两个问题解答

一、如何访问类中的槽

访问类实例的槽主要有三种方式:

  • 使用访问器(Accessor):如果定义槽时指定了:accessor参数(比如例子里的:accessor x),可以直接用访问器函数读写槽值。读取用(x 实例对象),修改用(setf (x 实例对象) 新值)。
  • 使用slot-value函数:这是通用的底层方式,不管有没有定义访问器都能用。读取槽值用(slot-value 实例对象 '槽名),修改用(setf (slot-value 实例对象 '槽名) 新值)。
  • 使用只读/只写函数:如果定义槽时用:reader(只读)或:writer(只写),比如:reader get-x,读取就用(get-x 实例对象);:writer set-x,修改就用(set-x 新值 实例对象),不过:accessor其实是同时定义了:reader和:writer,更常用。

二、能否基于一个槽定义另一个槽(类似let*的效果)

你给出的写法无法运行,因为:initform里的x并不是指向当前实例的槽,而是引用当前环境中的变量(比如全局变量),不是实例的x槽。要实现依赖其他槽初始化的效果,有几种可靠的方法:

方法1:使用初始化后的钩子(:after方法)

这是最稳妥的方式,因为Common Lisp不保证槽的初始化顺序,而:after方法会在实例所有槽初始化完成后执行:

(defclass foo ()
  ((x :initform 1 :accessor x)
   (y :accessor y))) ; 先不指定initform

(defmethod initialize-instance :after ((obj foo) &key)
  (setf (y obj) (+ 1 (x obj)))) ; 初始化完成后设置y的值

方法2:用计算式访问器(不存储y的值)

如果y的值始终依赖x,可以把y做成一个方法,每次访问时动态计算,不需要把它作为槽存储:

(defclass foo ()
  ((x :initform 1 :accessor x)))

(defmethod y ((obj foo))
  (+ 1 (x obj))) ; 每次调用(y obj)时计算值

方法3:(不推荐)直接在:initform中用slot-value

如果能确保依赖的槽先初始化(比如你明确知道类定义中槽的顺序),可以用slot-value直接访问实例的槽,但这种方式依赖实现细节,移植性差:

(defclass foo ()
  ((x :initform 1 :accessor x)
   (y :initform (+ 1 (slot-value self 'x)) :accessor y)))

注意:这里的self是Common Lisp中在:initform里可以引用当前实例的特殊变量(部分实现支持,标准允许在初始化表达式中访问实例),但因为初始化顺序不确定,这个写法可能在某些环境下出错,所以不推荐。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 06:32:45