6

我有一堂有很多插槽的课。我还有一个构建器函数来创建该类的对象,以便将以下列表传递'(:id "john" :name "John Doe" :age 42)给该函数将构造一个具有这些插槽值的新对象。我将使用该函数生成多个对象,使用列表列表。

如何将关键字 like 转换为可以使用:id的插槽名称SLOT-VALUE

谢谢。

4

4 回答 4

10

如果关键字是类的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>)
于 2012-09-18T06:21:45.310 回答
4

和函数将对您有所帮助find-symbolsymbol-name如果defclassslot-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)))

如果defclassslot-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 的名称。

于 2012-09-17T23:50:29.113 回答
1

我意识到这已经很老了,但我认为这里最重要的一点是:

不要这样用slot-value

要获取访问器,请使用:accessoror:reader插槽选项,并将值传递给构造函数,请使用:initarg

(defclass foo ()
  ((bar :accessor foo-bar :initarg :bar)))

这意味着:创建一个 getter 方法和一个名为 的 setf 扩展器foo-bar,并使用一个名为:barto的关键字参数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. 那是一个高级话题。

于 2017-07-18T19:50:54.213 回答
-1

我对 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))
于 2017-07-18T14:41:31.760 回答