我有一堂有很多插槽的课。我还有一个构建器函数来创建该类的对象,以便将以下列表传递'(:id "john" :name "John Doe" :age 42)
给该函数将构造一个具有这些插槽值的新对象。我将使用该函数生成多个对象,使用列表列表。
如何将关键字 like 转换为可以使用:id
的插槽名称SLOT-VALUE
?
谢谢。
我有一堂有很多插槽的课。我还有一个构建器函数来创建该类的对象,以便将以下列表传递'(:id "john" :name "John Doe" :age 42)
给该函数将构造一个具有这些插槽值的新对象。我将使用该函数生成多个对象,使用列表列表。
如何将关键字 like 转换为可以使用:id
的插槽名称SLOT-VALUE
?
谢谢。
如果关键字是类的initargs,那么您可以MAKE-INSTANCE
通过以下方式调用APPLY
:
(defclass person ()
((id :initarg :id )
(name :initarg :name)
(age :initarg :age )))
CL-USER > (mapcar
(lambda (initargs)
(apply #'make-instance 'person initargs))
'((:id "john" :name "John Doe" :age 42)
(:id "mary" :name "Mary Doe" :age 42)))
(#<PERSON 402027AB7B> #<PERSON 402027AC33>)
和函数将对您有所帮助find-symbol
。symbol-name
如果defclass
和slot-value
发生在同一个包中,您可以按如下方式使用这些函数:
(defclass person ()
((id :initarg :id)
(name :initarg :name)
(age :initarg :age)))
(slot-value (make-instance 'person :id "john" :name "John Doe" :age 42)
(find-symbol (symbol-name :id)))
如果defclass
和slot-value
发生在两个不同的包中,您需要给出发生find-symbol
的包的名称defclass
:
(in-package #:common-lisp-user)
(defpackage #:foo
(:use #:common-lisp)
(:export #:person))
(defpackage #:bar
(:use #:common-lisp #:foo))
(in-package #:foo)
(defclass person ()
((id :initarg :id)
(name :initarg :name)
(age :initarg :age)))
(in-package #:bar)
(slot-value (make-instance 'person :id "john" :name "John Doe" :age 42)
(find-symbol (symbol-name :id) 'foo))
(find-symbol name &optional (package (sane-package)))
功能:返回 PACKAGE 中名为 STRING 的符号。如果找到这样的符号,则第二个值是 :INTERNAL、:EXTERNAL 或 :INHERITED 以指示如何访问该符号。如果没有找到符号,那么这两个值都是 NIL。
(symbol-name symbol)
功能:以字符串形式返回 SYMBOL 的名称。
我意识到这已经很老了,但我认为这里最重要的一点是:
不要这样用slot-value
!
要获取访问器,请使用:accessor
or:reader
插槽选项,并将值传递给构造函数,请使用:initarg
:
(defclass foo ()
((bar :accessor foo-bar :initarg :bar)))
这意味着:创建一个 getter 方法和一个名为 的 setf 扩展器foo-bar
,并使用一个名为:bar
to的关键字参数make-instance
来初始化此插槽的值。
现在您可以像这样实例化这样的对象:
(make-instance 'foo :bar "quux")
或者,如果你得到一个 initargs 的属性列表(正如 Rainer 已经展示的那样):
(let ((initargs (list :bar "quux"))) ; getting this from somewhere
(apply #'make-instance 'foo initargs))
然后你可以得到这样的值:
(foo-bar some-foo)
并setf
像往常一样设置它:
(setf (foo-bar some-foo) "wobble")
如果使用:reader
而不是:accessor
,则不允许设置。这对于传达不变性的意图通常很有用。
Slot-value
确实适用于对象生命周期中的特殊情况,例如在玩弄initialize-instance
. 那是一个高级话题。
我对 CL 这种愚蠢的解决方案是:
(defun locate-symbol
(inst kw)
(let* ((slot-name (symbol-name kw))
(slot-def (find slot-name
(clos:compute-slots (class-of inst))
:test #'(lambda (name sd)
(string= name
(symbol-name (clos:slot-definition-name sd)))))))
(if slot-def
(clos:slot-definition-name slot-def)
(error "Can't find a slot definition named ~s." slot-name))))
(defun gets
(self slot-name)
"Get a value of a slot by its name (keyword)"
(slot-value self (locate-symbol self slot-name)))
(defun sets!
(self slot-name value)
"Set a value of a slot by its name (keyword)"
(setf (slot-value self (locate-symbol self slot-name))
value))
所以现在你可以这样做:
(defvar obj (make-instance '<some-class>))
(sets! obj :some-slot "some value")
(format t "-> ~a~%" (gets obj :some-slot))