Common lisp 如何将关键字转换为适合访问插槽的符号?

Common lisp 如何将关键字转换为适合访问插槽的符号?,common-lisp,Common Lisp,我有一个有很多槽的班。我还有一个构建器函数来创建该类的对象,这样,将下面的列表”(:id“john”:name“john Doe”:age 42)传递给该函数将使用这些值构造一个新对象。我将使用该函数生成多个对象,使用列表列表 如何将关键字(如:id转换为slot-VALUE可以使用的插槽名称 谢谢。查找符号和符号名称功能将对您有所帮助。如果defclass和slot value发生在同一个包中,则可以按如下方式使用这些功能: (定义人员() ((id:initarg:id) (名称:inita

我有一个有很多槽的班。我还有一个构建器函数来创建该类的对象,这样,将下面的列表
”(:id“john”:name“john Doe”:age 42)
传递给该函数将使用这些值构造一个新对象。我将使用该函数生成多个对象,使用列表列表

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


谢谢。

查找符号和符号名称功能将对您有所帮助。如果
defclass
slot value
发生在同一个包中,则可以按如下方式使用这些功能:

(定义人员()
((id:initarg:id)
(名称:initarg:name)
(年龄:initarg:age)))
(插槽值(生成实例的人:id“john”:name“john Doe”:42岁)
(查找符号(符号名称: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))

(查找符号名称和可选(软件包(sane软件包))

函数:返回包中名为STRING的符号。如果找到这样的符号,则第二个值为:INTERNAL、:EXTERNAL或:INHERITED,以指示如何访问该符号。如果未找到符号,则两个值均为零

(符号名称符号)

函数:以字符串形式返回符号的名称

如果关键字是类的initargs,那么您可以通过
APPLY
调用
MAKE-INSTANCE

(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>)
(定义人员()
((id:initarg:id)
(名称:initarg:name)
(年龄:initarg:age)))
CL-USER>(地图车)
(lambda(initargs)
(应用#“创建实例”person initargs))
(:id“john”:name“john Doe”:42岁)
(:id“mary”:姓名“mary Doe”:42岁)
(# #)

我对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))
(defvar obj(生成实例'))
(设置!对象:某个插槽“某个值”)
(格式t->~a~%“(获取对象:某个插槽))

我意识到这一点很古老,但我认为最重要的一点是:

不要那样使用
插槽值

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

(defclass foo ()
  ((bar :accessor foo-bar :initarg :bar)))
这意味着:创建一个名为
foobar
的getter方法和setf扩展器,并使用名为
:bar的关键字参数来
生成实例
来初始化此插槽的值

现在您可以像这样实例化这样的对象:

(make-instance 'foo :bar "quux")
(foo-bar some-foo)
或者,如果您得到initargs的属性列表(如Rainer所示):

然后可以获得如下值:

(make-instance 'foo :bar "quux")
(foo-bar some-foo)
并像往常一样使用
setf
进行设置:

(setf (foo-bar some-foo) "wobble")
如果使用
:reader
而不是
:accessor
,则不允许进行设置。这通常有助于传达不变性的意图


Slot value
确实适用于对象生命周期中的特殊情况,例如使用
初始化实例的方法时。这是一个高级主题。

我喜欢你的“更实用”的方法。@WhiteCat是你关于如何使用这些列表调用
make instance
的问题?我对你的问题有不同的理解,因为问题的标题和主体说“我如何将关键字转换为适合访问插槽的符号?”和“我如何将关键字(如:id)转换为
slot-VALUE
可以使用的插槽名称?”。如果您的真正目标只是调用
生成实例
,而不是
插槽值
,那么Rainer Joswig的解决方案就是一条出路。@dkim:我的问题与书面内容完全一样,您的回答是正确的。我以前试过使用
(查找符号…
),但使用了关键字作为包,所以不起作用。Rainer的回答给了我另一个视角,一个我没有考虑的问题,那就是正确的解决方案。谢谢两位。