6

I have a class with a number of slots. I also have a builder function to make objects of that class such that passing the following list '(:id "john" :name "John Doe" :age 42) to that function will construct a new object with those slots values. I will use that function to generate more than one object, using a list of lists.

How can I convert from a keyword like :id to a slot name that SLOT-VALUE can use?

Thanks.

4

4 に答える 4

10

キーワードがクラスのinitargsMAKE-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-symboland関数はあなた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)))

2つの異なるパッケージで発生する場合はdefclass、発生するパッケージの名前をslot-value指定する必要があります。find-symboldefclass

(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という名前のシンボルを返します。そのようなシンボルが見つかった場合、2番目の値は:INTERNAL、:EXTERNAL、または:INHERITEDであり、シンボルへのアクセス方法を示します。シンボルが見つからない場合、両方の値はNILです。

(symbol-name symbol)

機能:SYMBOLの名前を文字列として返します。

于 2012-09-17T23:50:29.113 に答える
1

これはかなり古いことだと思いますが、ここで最も重要なポイントは次のとおりです。

そのように使用しないでくださいslot-value

アクセサーを取得するには、:accessorまたは:readerスロットオプションを使用し、コンストラクターに値を渡すには、次を使用します:initarg

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

つまり、という名前のgetterメソッドとsetfエクスパンダを作成し、このスロットの値を初期化するためにfoo-barという名前のキーワード引数を使用します。:barmake-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 に答える