【问题标题】:Which lisp implementations allow me to modify code at runtime?哪些 lisp 实现允许我在运行时修改代码?
【发布时间】:2014-01-10 02:34:14
【问题描述】:

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”。

【问题讨论】:

  • 我不知道具体的实现 - 但寻找那些在评估时不编译的实现。所以这将排除 SBCL 例如(可能还有很多现代的 Common Lisp 实现)。
  • @verdammelt:见sbcl.org/manual/#Interpreter
  • 这不是一个完整的副本,因为我特别要求实现。现在我知道在 CLisp 和 SBCL 中是可能的。
  • @RainerJoswig 我已更正 - 谢谢!

标签: scheme lisp common-lisp self-modifying


【解决方案1】:

这可能不是最优雅的方法,但在常见的 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 数据结构而不是可以动态操作和执行的事实。

【讨论】:

  • 鉴于您实际上并没有从 FOO 中提取源代码,而只是将源代码存储在 F 中并对其进行评估以定义函数——您不能这样做吗? Common Lisp 的实现? (另外,请注意,frot defun 表单末尾有一个不匹配的右括号。)
  • @svk 我会这么认为,因为它是相当普通的 ANSI Lisp。然而,我只验证了它在 Clisp 和 sbcl 上的行为。这两种方法都有效,sbcl 发出了关于分配给f 的警告。
【解决方案2】:

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

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

将以下内容添加到您的代码中,然后在循环中添加 (update-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)))))

这是使用编辑器as in this video实时重新编译这一事实的一种方式(抱歉插入我自己的视频)

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

[更多详情] 上面代码中的 continuable 宏会捕获任何错误,并为您提供忽略它并继续的选项。我发现这真的很有帮助,而且我经常在 repl 中犯错误,我不想从错误中“中止”,因为这会中止我的主循环。

【讨论】:

    【解决方案3】:

    如果您按照描述的方式修改代码,那么您对代码的结构有所了解。既然你知道代码的结构,你可以参数化那个结构

    (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)))
    

    【讨论】:

      猜你喜欢
      • 2017-03-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-04
      • 1970-01-01
      相关资源
      最近更新 更多