我仍然无法完全弄清楚您要问什么问题,但我想您想知道如何在看不到绑定的函数内修改范围内的绑定。所以这里是这个问题的答案。
首先要了解的是现代编程语言中的作用域非常简单:如果您可以看到绑定(名称和值之间的关联),那么您就可以访问它,如果它是可变的,您可以对其进行变异.前现代的编程语言有各种神秘的规则,这些规则基于很久以前在微型计算机上的易于实施来限制这一点(我们都被 PDP-11 的遗留问题所诅咒),但现代的编程语言扫除了这一切。从这个意义上说,Common Lisp 主要是一种现代编程语言。
因此,您需要做的是以某种方式捕获绑定,然后将该捕获的绑定传递给您要调用的任何函数,在那里可以访问或改变它。捕获绑定的方式是使用函数。
所以,这里有一个如何在 CL 中执行此操作的简单示例:
(defun foo (orig new)
(let ((x orig))
(bar (lambda (&optional (value nil valuep))
(if valuep
(setf x value)
x))
new)
x))
(defun bar (c new)
(format t "~&initially ~S~%" (funcall c))
(funcall c new)
(format t "~&then ~S~%" (funcall c)))
在这段代码中,由bar 的第一个参数创建的函数可以访问x 的绑定,并且如此编写以便不带参数调用它会返回绑定的值,而使用一个参数将设置该值。以下是实际操作:
CL-USER 5 > (foo 1 2)
initially 1
then 2
2
所以你可以看到这是有效的:x 的绑定通过调用捕获它的函数来修改。
但这在语法上很笨拙:如果我们能避免所有这些显式的 funcalls 和 lambdas 那就太好了(我们可以在 Lisp-1 中避免前者,但它仍然不是很好)。所以这里有一些代码可以做到这一点(下面的解释):
(defmacro capture (binding)
"Capture a binding"
(let ((value (make-symbol "VALUE"))
(valuep (make-symbol "VALUEP")))
`(lambda (&optional (,value nil ,valuep))
(if ,valuep
(setf ,binding ,value)
,binding))))
(defun captured (c &optional (value nil valuep))
"Return the value of a captured binding, or set it"
(if valuep
(funcall c value)
(funcall c)))
(defsetf captured captured)
好的,所以宏capture 只是语法糖,它创建了一个与原始代码中的函数相同的函数。它必须是一个宏,因为该函数需要在它所捕获的绑定范围内创建。
captured 是一个微不足道的函数,它只是以适当的方式调用capture 创建的函数:所以与其说(funcall c),不如说(captured c)。
最后defsetf 表单教setf 如何设置捕获的绑定,所以(setf (captured x) y) 将起作用。
这里是上述 foo 和 bar 函数的重新实现,它们使用了这个:
(defun foo (orig new)
(let ((x orig))
(bar (capture x) new)
x))
(defun bar (c new)
(format t "~&initially ~S~%" (captured c))
(setf (captured c) new)
(format t "~&then ~S~%" (captured c)))
我认为这显然比上面所有明确的funcalls 和lambdas 更容易阅读。它的工作方式相同:
CL-USER 6 > (foo 1 2)
initially 1
then 2
2
顺便说一句,您当然可以捕获表达式,而不仅仅是变量绑定,只要 setf 知道如何处理它们(只要它们是所谓的“位置”):
(defun fish (l)
(bone (capture (car l)))
l)
(defun bone (c)
(setf (captured c) 'bone))
现在
CL-USER 13 > (fish (list 1 2))
(bone 2)