【问题标题】:How can I convert a keyword to a symbol suitable to access a slot?如何将关键字转换为适合访问插槽的符号?
【发布时间】:2012-09-17 22:23:46
【问题描述】:

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

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

谢谢。

【问题讨论】:

    标签: common-lisp


    【解决方案1】:

    如果关键字是类的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>)
    

    【讨论】:

    • 我喜欢你的“更实用”的方法。
    • @WhiteCat 您是否有关于如何使用这些列表调用make-instance 的问题?我对您的问题的理解不同,因为问题的标题和正文说“如何将关键字转换为适合访问插槽的符号?”和“如何从 :id 之类的关键字转换为 SLOT-VALUE 可以使用的插槽名称?”。如果您的真正目标只是打电话给make-instance,而不是slot-value,那么Rainer Joswig 的解决方案是一种可行的方法。
    • @dkim:我的问题和写的完全一样,你回答正确。我之前尝试过(find-symbol...),但使用 KEYWORD 作为包,所以它不起作用。 Rainer 的回答只是给了我另一个观点,一个我没有考虑过的观点,这是正确的解决方案。谢谢。
    【解决方案2】:

    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 &amp;optional (package (sane-package)))

    功能:返回PACKAGE中名为STRING的符号。如果找到这样的符号,则第二个值是 :INTERNAL、:EXTERNAL 或 :INHERITED 以指示如何访问该符号。如果没有找到符号,那么这两个值都是 NIL。

    (symbol-name symbol)

    功能:以字符串形式返回 SYMBOL 的名称。

    【讨论】:

      【解决方案3】:

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

      不要那样使用slot-value

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

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

      这意味着:创建一个 getter 方法和一个名为 foo-bar 的 setf 扩展器,并使用一个名为 :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 的方法时。这是一个高级主题。

      【讨论】:

        【解决方案4】:

        我对这种愚蠢的 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))
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-04-12
          • 2015-07-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-10-31
          相关资源
          最近更新 更多