也没有通用设置器,但是使用 SETF 时,您应该在使用DEFINE-SETF-EXPANDER时提供一个。我想你可以定义等效的lens/functional references。对于另一种方法,Clojure 定义了一个更新函数(借用其他语言,我知道它存在于 Prolog 中),它也不是通用的。FSET库提供了一些功能结构,特别是关联映射,其工作方式与 Clojure 中的类似。
假设您暂时将自己限制在结构中:
(defstruct foo a b c)
(defstruct bar x y z)
(defparameter *test*
(make-foo :a (make-bar :x 0 :y 0 :z 0)
:b 100
:c "string"))
;; *test* is now
;; #S(FOO :A #S(BAR :X 0 :Y 0 :Z 0) :B 100 :C "string")
以下方法制作副本,它依赖于copy-structure
and slot-value
,虽然不是标准的,但得到了很好的支持:
(defun update (structure keys value)
(if (endp keys)
value
(destructuring-bind (key &rest keys) keys
(let ((copy (copy-structure structure)))
(setf (slot-value copy key)
(update (slot-value copy key)
keys
value))
copy))))
您可以*test*
按如下方式更新:
(update *test* '(a z) 10)
这是一个跟踪:
0: (UPDATE #S(FOO :A #S(BAR :X 0 :Y 0 :Z 0) :B 100 :C "string") (A Z) 10)
1: (UPDATE #S(BAR :X 0 :Y 0 :Z 0) (Z) 10)
2: (UPDATE 0 NIL 10)
2: UPDATE returned 10
1: UPDATE returned #S(BAR :X 0 :Y 0 :Z 10)
0: UPDATE returned #S(FOO :A #S(BAR :X 0 :Y 0 :Z 10) :B 100 :C "string")
为了概括,您可以接受一个函数而不是一个值,这样您就可以incf
通过部分应用更新函数来实现等效的#'1+
(生成的闭包将接受一个键列表)。
此外,您需要泛化复制操作,这可以通过泛型函数实现。同样,您可以使用其他类型的访问器,并替换slot-value
为通用access
函数(有一个(setf access)
操作)。但是,如果您想共享一些数据,这种通用的 copy/setf 方法可能会很浪费。例如,您只需要复制列表中指向您的数据的部分,而不是复制它后面的 cons 单元格。
您可以定义一些工具来定义自定义更新程序:
(defmethod perform-update (new (list list) position)
(nconc (subseq list 0 position)
(list new)
(nthcdr (1+ position) list)))