假设我有一个运行我的 Common Lisp 代码的进程的 REPL。它可能正在运行 SWANK/SLIME。
我想在我的实时进程中更新一个用 defun 定义的函数。该函数可能已经捕获了 let 绑定(bind)中的一些变量。本质上,这个函数是一个闭包。
如何更新闭包中的代码而不丢失它捕获的数据?
2019-11-03:我在下面选择了一个答案,但我建议阅读所有答案。每个人都有有趣的见解。
最佳答案
基本上你不能,这是避免在 LET 中使用 DEFUN 的原因之一。
可以创建一个新闭包并尝试将状态从旧闭包复制到新闭包中。
一个问题是可移植的 Common Lisp 不允许对闭包进行大量动态访问。人们不能“进入”闭包并从外部添加或替换某些东西。没有为闭包定义反射或内省(introspection)操作。
因此,您以后想要对闭包执行的所有操作都需要已经存在于闭包生成代码中。
假设您有这段代码:
(let ((foo 1))
(defun add (n)
n))
现在我们确定 add
是错误的,应该实际添加一些东西吗?
我们想要这样的效果:
(let ((foo 1))
(defun add (n)
(+ n foo)))
我们如何修改原始文件?我们基本上不能。
如果我们有:
(let ((foo 1))
(defun get-foo ()
foo)
(defun add (n)
n))
我们可以这样做:
(let ((ff (symbol-function 'get-foo))
(fa (symbol-function 'add)))
(setf (symbol-function 'add)
(lambda (n)
(+ (funcall fa n) (funcall ff)))))
这然后定义了一个新函数 add
,它可以通过旧函数访问闭包值 - 在它自己的闭包中捕获。
风格
不要使用 LET 封闭的 DEFUN:
- 它们很难修改
- 编译器不会将 DEFUN 视为顶级形式
- 影响难以控制
- 我们想对多次加载代码做什么?
- 它们很难调试
- Common Lisp 有处理全局状态的方法。
关于lambda - 如何在保留捕获的变量的同时更改 lambda 中的代码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58619515/