在程序运行时更改程序

2023-11-29

不确定这是 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(使用前将#替换为@)

在程序运行时更改程序 的相关文章

随机推荐