5

我想setf根据某个变量来定义结构的不同字段。我决定使用以下方法:

使用字段的访问器名称生成一个字符串:

(setq my-string (format nil "STRUCT-ESTADISTICAS-NUM-~S" x))

然后使用带有funcall的实习生:

(funcall (intern my-string) *estadisticas*)

此调用返回结构字段的正确值,但如果我尝试setf修改此值,它会抱怨说:

(setf (funcall(intern my-string) *estadisticas*) 0)
Error: `(SETF FUNCALL)' is not fbound

我可以理解为什么它不起作用,但是我找不到修改结构字段的方法。任何的想法?谢谢你。

4

2 回答 2

5

您想通过其名称调用结构的编写器函数,编写器的名称是 list (setf accessor-name);所以

(funcall (fdefinition (list 'setf (intern my-string))) 0 estadisticas)

编辑:

没有看到你的代码的其余部分,很难理解出了什么问题。在 SBCL 上,这对我有用:

(defstruct point x y)
(let ((point (make-point :x 1 :y 2)))
  (funcall (fdefinition (list 'setf (intern "POINT-X"))) 10 point)
  point)

以上评估为

#S(POINT :X 10 :Y 2),

正如预期的那样。

于 2010-09-04T10:57:31.097 回答
5

动机:

结构是一个相对低级的设施。“速度”是一个重要的设计目标。标准不支持通过编写器函数进行间接调用(正如我所读的那样)。今天,使用 CLOS 作为默认设置,除非需要更高的结构效率(有时可以更快地读取和写入具有结构的插槽)。

第一风格:

不要使用 INTERN,使用 FIND-SYMBOL。还要指定包,否则 FIND-SYMBOL 会使用 *package* 的运行时值作为包

第二 - DESTRUCT

如果我正确阅读了 ANSI CL 标准,那么 DEFSTRUCT 并不是像 DEFCLASS 那样为插槽创建写入函数。

CL-USER 24 > (defstruct foo bar baz)
FOO

CL-USER 25 > #'(setf foo-bar)

Error: Undefined function (SETF FOO-BAR) in form (FUNCTION (SETF FOO-BAR)).

因此,构造这样的名称 (SETF FOO-BAR) 并尝试为其查找函数将失败,因为 DEFSTRUCT 没有定义这样的函数。

用户代码 (setf (foo-bar some-struct) 42) 的工作原理是基于 DEFSTRUCT 提供的已定义的 SETF 扩展,而不是基于已定义的 SETF 访问器函数。

一些 Common Lisp 实现可能会提供编写器函数作为 ANSI CL 的非标准扩展。

可能的解决方案

a) 使用 CLOS 类, DEFCLASS 做你想做的事

b) 自己编写 writer 函数

(defun (setf foo-bar) (new-value struct)
   (setf (foo-bar struct) new-value))

现在:

(funcall (fdefinition '(setf foo-bar)) 300 *foo*)

上面然后工作。

c) (SETF SLOT-VALUE) - 一些实现的另一个非标准特性。

在 Common Lisp 的某些实现中,这不仅适用于 CLOS 类,也适用于结构:

(setf (slot-value some-struct 'bar) 42)

我不确定 Allegro CL 是否支持这一点,但这很容易找到。

于 2010-09-07T07:16:57.957 回答