如何在setf place上执行功能

时间:2019-02-07 22:30:12

标签: common-lisp setter clos setf

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

(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,但是问题仍然存在,因为它们也是宏。
  • 我考虑过用于声明其他宏的宏,也可以考虑symbol-macrolet。

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

谢谢。

3 个答案:

答案 0 :(得分: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"

使用SETF和APPLY来隐藏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被设计为动态的和反射性的,以允许这些事情。

答案 1 :(得分:2)

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

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

但是,通常只有在处理对象内部问题时才明智地直接使用slot-value(例如initialize-instance方法/组合;或者您正在研究某种序列化机制)。

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

答案 2 :(得分:1)

我同意应该使用hash-table之类的替代结构。但是要扩展关于slot-value的{​​{3}}答案,工作代码很简单:

(setf (slot-value obj 'attr) "some-value")