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

标签 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))))

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

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

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

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

有什么好的方法可以让它发挥作用吗?
谢谢。

最佳答案

像这样的形式

`(when (not (string-empty-p arg))
      ,@body
      (message arg))

相当于这样的东西:

(append '(when (not (string-empty-p arg)))
          body
          ((message arg))))

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

(defun make-anonymous-func (&rest body)
  (lambda (arg)
    (append '(when (not (string-empty-p arg)))
            body
            `((message arg)))))

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

(做你想做的事情的一个可怕的方法是评估源代码的eval。但是有很多原因导致这是一个非常糟糕的答案,所以我不会在这里深入讨论。)

做你想做的事情的一般方法是依赖词法范围。方便地 elisp 现在词法作用域,所以你实际上可以这样做。以下两个示例假定 lexical-binding 为真。

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

(defun make-anonymous-func (msg)
  (lambda (arg)
    (when (not (string-empty-p arg))
      (message msg)
      (message arg))))

第二种方法是,如果您希望该函数运行一些通用代码:这样做的方法是向它传递一个要调用的函数:

(defun make-anonymous-func (f)
  (lambda (arg)
    (when (not (string-empty-p arg))
      (funcall f arg)
      (message arg))))

现在

(funcall (make-anonymous-func
          (lambda (m)
            (message "foo")
            (sit-for 1)))
         "x")

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

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

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

现在

(define-messaging-function foo (x)
  (message "foo"))

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

关于lisp - 如何在 Emacs Lisp 中动态更改 lambda 内容?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72768344/

相关文章:

macros - 宏参数的解构是 "really needed"吗?

recursion - SICP 中的迭代阶乘过程

lisp - (方案)递归函数来计算某些列表的所有可能组合?

lisp - 加载函数库的最佳方式

Elisp深度复制-精简

email - 在 elisp 中发送电子邮件的最简单、最便携的方式是什么?

function - 了解结构(段落内容)和继续教育

emacs - elisp 中的属性列表

emacs - 为什么我在 native 使用 Google Drive API 时收到 "The authenticated user has not installed the app with client id"错误?

emacs - Emacs Lisp 的 REPL