Scheme 哪些lisp实现允许我在运行时修改代码?

Scheme 哪些lisp实现允许我在运行时修改代码?,scheme,lisp,common-lisp,self-modifying,Scheme,Lisp,Common Lisp,Self Modifying,Lisp是同音符号,这意味着代码可以被视为数据。哪些实现允许我在运行时这样做?以下是伪代码中我的意思示例: (defun (my-func) (display "foo ") (display "bar ") (display "baz ")) (defun (main-loop) (my-func) (swap (first my-func) (second my-func)) (main-loop)) 它应该反复输出“foo-bar-baz-bar-foo-baz

Lisp是同音符号,这意味着代码可以被视为数据。哪些实现允许我在运行时这样做?以下是伪代码中我的意思示例:

(defun (my-func)
  (display "foo ")
  (display "bar ")
  (display "baz "))

(defun (main-loop)
  (my-func)
  (swap (first my-func) (second my-func))
  (main-loop))

它应该反复输出“foo-bar-baz-bar-foo-baz”。

这可能不是最优雅的方法,但在常见的Lisp中,您可以执行以下操作:

> (setq f '(defun foo () (princ "foo ") (princ "bar ") (princ "baz ")))
(DEFUN FOO NIL (PRINC "foo ") (PRINC "bar ") (PRINC "baz "))
> (eval f)
FOO
> (foo)
foo bar baz
NIL
> (defun frot ()
        ; Call foo (stored in f)
        (funcall (eval f))
        ; Swap the 1st and 2nd statements in foo
        (setf tmp (cadddr f))
        (setf (cadddr f) (cadr (cdddr f)))
        (setf (cadr (cdddr f)) tmp)))
FROT
> (frot)
foo bar baz
(PRINC "foo ")
> (frot)
bar foo baz
(PRINC "bar ")
> (frot)
foo bar baz
(PRINC "foo ")

这将Lisp函数存储在
f
中,而不是就地执行,但它确实说明了一个事实,即Lisp程序本身就是一个Lisp数据结构,可以动态操作和执行。

其他答案很好地涵盖了这个问题

但是,从实用的角度来看,如果您使用的是Common Lisp和Slime,并且希望能够从Emacs将代码编译到正在运行的程序中,那么您需要告诉Swank从循环内部进行更新

(defmacro continuable (&body body)
  `(restart-case
       (progn ,@body)
     (continue () :report "Just Continue")))

(defun update-swank ()
  "Called from within the main loop, this keep the lisp repl working"
  (continuable
    (let ((connection (or swank::*emacs-connection*
                         (swank::default-connection))))
      (when connection
        (swank::handle-requests connection t)))))
将以下内容添加到代码中,然后在循环中添加(更新swank)

(defmacro continuable (&body body)
  `(restart-case
       (progn ,@body)
     (continue () :report "Just Continue")))

(defun update-swank ()
  "Called from within the main loop, this keep the lisp repl working"
  (continuable
    (let ((connection (or swank::*emacs-connection*
                         (swank::default-connection))))
      (when connection
        (swank::handle-requests connection t)))))
这是一种使用事实的方法,您可以使用编辑器重新编译live(很抱歉插入我自己的视频)

另一种方法(同样是Slime)是告诉它使用不同的线程进行通信。但是,我更喜欢前一种方法,因为opengl在跨线程使用时非常不稳定

[更多详细信息]
上面代码中的continuable宏捕获任何错误,并提供忽略该错误并继续的选项。我发现这非常有用,而且我经常在repl中出错,我不想从错误中“中止”,因为这会中止我的主循环。

如果您正在修改所描述的代码,那么您对代码的结构会有所了解。因为您知道代码的结构,所以可以参数化该结构

(define (foo-er foo bar baz)
  (lambda ()
     (display foo)
     (display bar)
     (display baz)))
然后,您可以通过显式传递参数来进行交换,如下所示:

(define (main-loop)
  (let looping ((foo "foo ") (bar "bar ") (baz "baz "))
    ((foo-er foo bar baz))
    (looping bar foo baz)))

> (main-loop)
foo bar baz bar foo baz foo bar baz ...
CommonLisp版本与此类似

如果您需要保留
my func

(define my-func #f)
(define (main-loop)
  (let looping ((foo "foo ") (bar "bar ") (baz "baz "))
    (set! my-func (foo-er foo bar baz)
    (my-func)
    (looping bar foo baz)))

我不知道有什么具体的实现——但请寻找那些在评估时没有编译的实现。例如,这就排除了SBCL(可能还有很多现代常见的Lisp实现)。基本上是:@Verdamelt:see的副本,这不是一个完全的副本,因为我特别要求实现。现在我知道这在CLisp中是可能的,可能在SBCL中也是可能的。@RainerJoswig我被纠正了-谢谢!考虑到您实际上并不是从
FOO
中提取源代码,而是将源代码存储在
F
中,并对其进行求值以定义函数——您不能在任何Common Lisp实现中这样做吗?(另外,请注意,在
frot
defun表单的末尾有一个不匹配的右括号。)@svk我也这么认为,因为它相当普通的ANSI Lisp。然而,我只在Clisp和sbcl上验证了它的行为。这两种方法都有效,sbcl发出了关于分配给
f
的警告。