如何访问类中的槽?能否在类定义中像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
相关产品推荐
相关产品推荐

