也没有通用的二传手,但是对于 SETF,您应该在使用 DEFINE-SETF-EXPANDER 时提供一个。我想你可以定义 lenses/functional references 的等价物。对于另一种方法,Clojure 定义了一个 update 函数(借用其他语言,我知道它存在于 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 和slot-value,虽然不是标准的,但它是well supported:
(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")
为了概括,你可以接受一个函数而不是一个值,这样你就可以通过使用#'1+ 部分应用更新函数来实现incf 的等价物(生成的闭包将接受一个 钥匙)。
此外,您需要泛化 copy 操作,这可以通过泛型函数实现。同样,您可以使用其他类型的访问器,并将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)))