如何在Emacs Lisp中创建临时函数

25

我正在调用一些繁琐的函数,但参数将在运行时确定。我编写了一个简单的函数来保持代码DRY,但给它命名是不必要的。我没有在其他地方使用此函数。

我尝试以Scheme的方式实现它,但是会出现void-function错误:

(let ((do-work (lambda (x y z)
                  (do-x x)
                  (do-y y)
                  ;; etc
                  )))
  (cond (test-1 (do-work 'a 'b 'c))
        (test-2 (do-work 'i 'j 'k))))

我可以把所有内容都放进一个apply中(例如:(apply (lambda ...) (cond ...))),但那样不太容易阅读。有更好的办法吗?

3个回答

29

与Scheme不同,Emacs Lisp有变量和函数的分离命名空间(即它是一个'Lisp2',而不是'Lisp1';请参见Technical Issues of Separation in Function Cells and Value Cells以了解这些术语的起源和含义)。

您需要使用funcallapply来调用存储在变量中的lambda(或其他函数)。

(cond (test-1 (funcall do-work 'a 'b 'c))
      (test-2 (funcall do-work 'i 'j 'k))

使用funcall,如果您总是发送相同数量的参数。使用apply,如果您需要能够发送可变数量的参数。
内部机制是每个符号都有多个“单元格”。使用哪个单元格取决于符号在计算形式中的位置。当一个符号是计算形式的第一个元素时,它的“函数”单元格被使用。在任何其他位置,它的“值”单元格被使用。在您的代码中,do-work 在其值单元格中具有函数。要将其作为函数访问,可以使用funcallapply。如果它在函数单元格中,您可以通过使用其名称作为计算形式的car来直接调用它。您可以使用fletlabelscl实现此目的。

21

使用(require 'cl)来调用 Common Lisp 包,然后使用 flet代替 let

(flet ((do-work (x y z)
          (do-x x)
          (do-y y)
          ;; etc
          ))
  (cond (test-1 (do-work 'a 'b 'c))
        (test-2 (do-work 'i 'j 'k))))

4
您可以按照 ANSI Common Lisp 的方式来实现这一点(尽管我认为有些 Emacs 开发人员会给您不好的眼神):
(flet ((do-work (x y z)
                (do-x x)
                (do-y y)
                ;; etc
                ))
  (cond (test-1 (do-work 'a 'b 'c))
        (test-2 (do-work 'i 'j 'k))))

不确定你是否需要先(require 'cl) (或者 cl-macs?)才能使用flet函数。如果你想定义递归函数,需要使用labels


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接