我有两个返回匿名函数的函数,除了几行之外,函数中的几乎所有内容都是相同的。
(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)
有没有一些好的方法可以让它发挥作用?
谢谢。
像这样的形式
\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
评估源代码。然而,有很多原因可以解释为什么这是一个非常糟糕的答案,所以我不会在这里讨论它。)
完成您要做的事情的一般方法是依赖词法范围。方便的是,elisp 现在具有词法范围,因此您实际上可以做到这一点。以下两个示例假设这lexical-binding
是正确的。
首先,如果您只想捕获某些变量绑定的值,那么您可以这样做:
\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
函数所做的操作。