不确定这是 emacs-SLIME 问题还是 CL 问题或 SBCL 问题。
我听说 Lisp 的交互特性允许在程序运行时更改程序。不知道这意味着什么,我尝试了以下操作,将其放在一个单独的文件中:
(defparameter repl-test-var 5)
(defun repl-test ()
(format t "repl-test-var is: ~a" repl-test-var)
(fresh-line)
(when (not (equal (read-line) "quit"))
(repl-test)))
然后我编译并运行(repl-test)
每次我按下回车键,我都会看到这个数字5
.
无需打字quit
在 REPL 中,我返回到我的文件并更改5
to a 6
并再次编译。回到 REPL,按 Enter 仍然显示5
。如果我输入quit
然后运行(repl-test)
再次,现在我看到了6
.
我还尝试过加载以及编译和加载的组合,然后使用 SLIME 快捷方式进行加载,直到我退出正在运行的程序然后再次启动它之后,它们才起作用。
我想要做的事情是不可能的还是需要在代码中执行另一个步骤?
我意识到这是一个微不足道的例子,但在更复杂的场景中我可能希望这样做。
在 Lisp 中替换函数并不意味着“自修改代码”。
如果在 Lisp 中执行函数,则指令指针保存对该函数的引用,因此该函数仍然是无法被垃圾收集器回收的活动对象。
当您重新定义函数时,这意味着一个新的函数对象与该名称相关联。当通过名称调用该函数时,将使用新函数。
但是,对正在进行的函数的现有调用将继续使用旧函数(不再附加到符号)。当最后一个线程停止执行该函数时,它将变成垃圾。
这与 Unix 中对已从目录结构中删除的打开文件的“最后关闭”非常相似。
该问题不仅出现在多线程中,而且出现在简单的递归中。如果正在执行的函数itself触发重新定义,则函数将继续使用旧的主体。此外,Lisp 允许递归函数中的自调用以避免通过名称绑定,而是使用直接机制。如果递归函数重新定义自身,则在同一调用中仍要进行的递归调用可能会继续进入同一主体。
更一般地说,Common Lisp 允许编译器在同一文件中的函数之间生成有效的调用。因此,您通常必须将运行代码的替换视为模块级别而不是单个函数级别。如果函数A和B在同一个模块中,并且A调用B,那么如果你只是替换B而不替换A,A可能会继续调用旧的B(因为B被内联到A中,或者因为A没有经过符号,但对 B) 使用更直接的地址。您可以声明函数notinline
来抑制这一点。
CL 具有许多功能,可以对编译和加载的语义提供大量控制,因此您可以获得应用程序中所需的精确行为。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)