1

我有一个包含一些符号和值的列表。目标是使用访问器设置类槽,其符号由列表提供:

(defclass my-class ()
 ((attr :accessor attr)))

(let ((to-call '(attr "some-value"))
      (obj (make-instance 'my-class)))
 (setf `(,(car to-call) obj) (cadr to-call)))

我试过通过宏:

(defmacro call-accessor (to-call)
 `(setf (,(car to-call) obj) "some-value"))

(let ((to-call '(attr "some-value"))
      (obj (make-instance 'my-class)))
 (call-accessor to-call))

这也失败了,因为to-call它是一个符号而不是一个列表。

  • eval不起作用,因为to-call它是一个词法变量;
  • 不可能let通过宏来给它列表;
  • 我已经尝试过with-slotswith-accessors但问题仍然存在,因为它们也是宏。
  • 我考虑过声明其他宏的宏,以及符号宏。

如何通过与列表中的符号对应的访问器设置插槽?

谢谢你。

4

3 回答 3

3

调用访问器函数

目标是用访问器设置类槽

访问器是一对函数。您可以通过 获取设置值的部分FDEFINITION。该函数的名称是一个列表(SETF accessor-name )。这是不寻常的:在这种情况下,Common Lisp 的函数名称不是符号,而是列表。

CL-USER 14 > (let ((to-call '(attr "some-value"))
                   (obj (make-instance 'my-class)))
               (funcall (fdefinition `(setf ,(first to-call)))
                        (second to-call)
                        obj)
               (describe obj))

#<MY-CLASS 40200614FB> is a MY-CLASS
ATTR      "some-value"

使用函数call-accessor

CL-USER 25 > (let ((to-call '(attr "some-value"))
                   (obj (make-instance 'my-class)))
               (flet ((call-accessor (obj to-call)
                        (funcall (fdefinition `(setf ,(first to-call)))
                                 (second to-call)
                                 obj)))
                 (call-accessor obj to-call)
                 (describe obj)))

#<MY-CLASS 402000220B> is a MY-CLASS
ATTR      "some-value"

使用带有 APPLY 的 SETF 来隐藏 FDEFINITION 调用

setf与计算访问器一起使用,可能需要使用apply表单和自定义函数。

类似的东西call-accessor自然会是一个函数,因为它执行运行时查找并获取值。如果在编译时就知道访问器,那么尝试使用宏会更有用。

CL-USER 23 > (let ((to-call '(attr "some-value"))
                   (obj (make-instance 'my-class)))
               (flet (((setf my-setter) (new-value object accessor) 
                        (funcall (fdefinition `(setf ,accessor))
                                 new-value
                                 obj)))
                 (flet ((call-accessor (obj to-call)
                          (setf (apply #'my-setter obj (list (first to-call)))
                                (second to-call))))
                   (call-accessor obj to-call)
                   (describe obj))))

#<MY-CLASS 40200009AB> is a MY-CLASS
ATTR      "some-value"

数据结构的选择

我认为计算访问器函数和类似函数是可以的。可能有一些用例。CLOS 被设计成动态和反射性的,以允许这些事情。

于 2019-02-08T08:10:01.640 回答
2

您可以使用setf,slot-value使用数据对中的插槽名称符号:

(let ((to-call '(attr "some-value")))
  (setf (slot-value obj (first to-call)) (second to-call)))

但是,slot-value直接使用通常只有在您争论对象内部时才有意义(例如初始化实例方法/组合;或者您可能正在研究某种序列化机制)。

如果不是这种情况,则您只是将对象用作关联数据结构。我建议改用 alist、plist 或 hash-map。

于 2019-02-07T23:01:18.337 回答
1

我同意hash-table应该使用像 a 这样的替代结构。但是为了扩展Svante关于 的答案slot-value,工作代码很简单:

(setf (slot-value obj 'attr) "some-value")
于 2019-02-08T03:38:45.807 回答