如何在 Emacs Lisp 中动态更改 lambda 内容?

tip*_*tto 2 lisp elisp

我有两个返回匿名函数的函数,除了几行之外,函数中的几乎所有内容都是相同的。

(defun foo ()
  (interactive)
  (lambda (arg)
    (when (not (string-empty-p arg))
      (message "foo")
      (message arg))))

(defun bar ()
  (interactive)
  (lambda (arg)
    (when (not (string-empty-p arg))
      (message "bar")
      (message arg))))
Run Code Online (Sandbox Code Playgroud)

因此,我创建了一个函数,该函数接收列表作为参数并返回一个匿名函数,如下所示:

(defun make-anonymous-func (&rest body)
  (interactive)
  (lambda (arg)
    `(when (not (string-empty-p arg))
      ,@body
      (message arg))))
Run Code Online (Sandbox Code Playgroud)

但是,当我执行 返回的函数时make-anonymous-func,lambda 中的列表被识别为列表文字,并且未正确评估。

(funcall (make-anonymous-func '(message "foo")) "bar")
; ===> (when (not (string-empty-p arg)) (message "foo") (message arg))
Run Code Online (Sandbox Code Playgroud)

有没有一些好的方法可以让它发挥作用?
谢谢。

ign*_*ens 7

像这样的形式

\n
`(when (not (string-empty-p arg))\n      ,@body\n      (message arg))\n
Run Code Online (Sandbox Code Playgroud)\n

相当于这样:

\n
(append '(when (not (string-empty-p arg)))\n          body\n          ((message arg))))\n
Run Code Online (Sandbox Code Playgroud)\n

所以你的函数可以这样重写:

\n
(defun make-anonymous-func (&rest body)\n  (lambda (arg)\n    (append '(when (not (string-empty-p arg)))\n            body\n            `((message arg)))))\n
Run Code Online (Sandbox Code Playgroud)\n

我认为现在很清楚为什么这行不通。更一般地说,反引号形式提供了一种简洁的方式来描述通过填充模板来构造 s 表达式的方式,这在宏中很有用,因为 s 表达式表示 Lisp 源代码,而宏本质上是其值为源代码的函数,以及在您想要从模板构造 s 表达式的其他地方。

\n

(做你想做的事情的一种可怕的方法是eval评估源代码。然而,有很多原因可以解释为什么这是一个非常糟糕的答案,所以我不会在这里讨论它。)

\n

完成您要做的事情的一般方法是依赖词法范围。方便的是,elisp 现在具有词法范围,因此您实际上可以做到这一点。以下两个示例假设这lexical-binding是正确的。

\n

首先,如果您只想捕获某些变量绑定的值,那么您可以这样做:

\n
(defun make-anonymous-func (msg)\n  (lambda (arg)\n    (when (not (string-empty-p arg))\n      (message msg)\n      (message arg))))\n
Run Code Online (Sandbox Code Playgroud)\n

第二种方法是如果您希望函数运行一些通用代码:执行此操作的方法是向其传递一个要调用的函数:

\n
(defun make-anonymous-func (f)\n  (lambda (arg)\n    (when (not (string-empty-p arg))\n      (funcall f arg)\n      (message arg))))\n
Run Code Online (Sandbox Code Playgroud)\n

现在

\n
(funcall (make-anonymous-func\n          (lambda (m)\n            (message "foo")\n            (sit-for 1)))\n         "x")\n
Run Code Online (Sandbox Code Playgroud)\n

将导致出现两条消息,但这次它们之间会有暂停,因此您将看到第一条消息。

\n

最后一种可能性,如果您想要做的是在编译时或定义时静态创建一堆类似的函数 \xe2\x80\x93 而不是在运行时动态 \xe2\x80\x93 ,那就是使用宏。

\n
(defmacro define-messaging-function (name args &rest forms)\n  ;; elisp has no destructuring, so check\n  (unless (and (listp args) (= (length args) 1)\n               (symbolp (car args)))\n    (error "bad arguments"))\n  (let ((arg (car args)))\n    `(defun ,name (,arg)\n       (when (not (string-empty-p ,arg))\n         ,@forms\n         (message ,arg)))))\n
Run Code Online (Sandbox Code Playgroud)\n

现在

\n
(define-messaging-function foo (x)\n  (message "foo"))\n
Run Code Online (Sandbox Code Playgroud)\n

定义foo为一个函数,在调用时,它将执行原始函数返回的foo函数所做的操作。

\n