在 emacs lisp 中使用 let 和 flet

Posted

技术标签:

【中文标题】在 emacs lisp 中使用 let 和 flet【英文标题】:let and flet in emacs lisp 【发布时间】:2010-11-14 21:27:26 【问题描述】:

我不知道您是否会称其为规范公式,但要绑定本地函数,GNU 手册建议我使用“flet”:

(defun adder-with-flet (x)
  (flet ( (f (x) (+ x 3)) )
    (f x))
)

但是,我偶然尝试了(在Scheme中玩了一会儿之后)以下表达式,其中我使用'let'将一个lambda表达式绑定到一个变量,如果我将函数传递给mapcar *,它也可以工作:

(defun adder-with-let (x)
  (let ( (f (lambda (x) (+ x 3))) )
    (car (mapcar* f (list x)) ))
)

这两个功能都有效:

(adder-with-flet 3)   ==> 6
(adder-with-let 3) ==> 6

为什么第二个有效?我找不到任何可以使用“let”将函数绑定到符号的文档。

【问题讨论】:

对于任何尝试此操作的人,请注意flet 在您使用的 emacs 版本中可能不可用,在这种情况下,请事先尝试 (require 'cl),如下所述(flet 是CommonLisp 的东西)。 哪个 GNU 手册推荐在这里使用flet 来自 Emacs 25.1.1 描述功能:flet 该宏自 24.3 起已过时;使用cl-flet' or cl-letf' 【参考方案1】:

与Scheme不同,Emacs Lisp是一个2-lisp,这意味着每个符号都有两个独立的绑定:值绑定和函数绑定。在函数调用 (a b c d) 中,第一个符号 (a) 使用函数绑定查找,其余的 (b c d) 使用值绑定查找。特殊形式 let 创建一个新的(本地)值绑定,flet 创建一个新的函数绑定。

请注意,是否使用值或函数绑定进行查找取决于(a b c d) 函数调用中的位置,而不是查找值的类型 .特别是,值绑定可以解析为函数。

在你的第一个例子中,你函数绑定f(通过flet),然后进行函数查找:

(f ...)

在第二个示例中,您将 f 值绑定到函数(通过 let),然后使用值查找:

(... f ...)

两者都有效,因为您在每种情况下都使用相同类型的绑定和查找。

http://en.wikipedia.org/wiki/Common_Lisp#Comparison_with_other_Lisps

【讨论】:

感谢您的解释!我明白了,这是值查找和函数查找之间的区别。我熟悉为函数和变量分别命名空间的惯例,但无法将其与 mapcar* 调用绑定为变量的函数联系起来。【参考方案2】:

我快速搜索了 Emacs lisp 手册,但找不到任何对 'flet 的引用,这并不奇怪,因为它是 cl 的一部分 - common-lisp package。

let 也会进行本地绑定,但不会绑定到该符号的 "function cell"。

即这有效:

(let ((myf (lambda (x) (list x x))))
  (eval (list myf 3)))

但是

(let ((myf (lambda (x) (list x x))))
  (myf 3))

失败并出现错误:“Lisp 错误:(void-function myf)”

另一方面,flet 确实绑定到了函数单元格,所以这是可行的:

(flet ((myf (x) (list x x)))
  (myf 3))

注意区别在于flet 允许您直接使用符号myf,而let 不允许 - 您必须使用一些间接来将函数从“值单元格”中取出并应用它适当的。

在您的示例中,“mapcar”与我使用 'eval 的效果相同。

【讨论】:

感谢您的回复!连同 zielaj 的解释,我看到了这个“评估”的东西也是如何工作的。是的, flet 似乎在 cl 扩展中;我最初读过在使用 flet 之前需要 (require 'cl) 但我想在较新的 emacs 中不再是这种情况...... cl 扩展随 Emacs 一起提供之前,人们是如何处理需要用 let-like 语义声明的函数定义的?是否只是接受 eval 是这样做的方式?【参考方案3】:

@d11wq 有 `funcall' 用于此目的。以下作品:

(defun adder-with-let (x)
  (let ((f #'(lambda (x) (+ x 3))))
    (funcall f 3)))

(adder-with-let 3) ;=> 6

【讨论】:

【参考方案4】:

如果您不想使用,则不必使用flet。您将函数放置在使用 let 定义的局部符号的函数单元中,如下例所示:

(let ((ALocalSymbol))
  (fset 'ALocalSymbol (lambda (x) (* 2 x)))
  (ALocalSymbol 4)
  )

评估这将返回 8。请注意 (let ((ALocalSymbol))...)ALocalSymbol 前面的引号。 setq 引用符号,fset 没有。

flet 是一种语法糖。使用普通的let 定义零值符号,允许您选择要设置的符号的哪个“单元格”。您可以使用setq 设置符号的值单元格或使用fset 设置函数单元格。

希望这会有所帮助,

巴勃罗

【讨论】:

这是完全错误的,将(单个)全局函数单元绑定到实习符号ALocalSymbol。 (只有 value 单元格是 let-bound。)如果您要创建一个 new (uninterned)符号,而不仅仅是 let-binding (的值),您可以这样做实习符号。 为了清楚起见:这段代码在全局函数命名空间中定义了一个函数ALocalSymbol。如果已经定义了同名的函数,它就会被破坏。 还请注意,如果您确实创建了一个非驻留符号,(ALocalSymbol 4) 仍将调用该驻留符号的函数,因此您需要 funcall(或类似的)您的非驻留符号/函数。 我才意识到你们是对的。我最终创建了一个全局符号!谢谢你的澄清。这实际上解释了为什么我写的一些代码不能正常工作。

以上是关于在 emacs lisp 中使用 let 和 flet的主要内容,如果未能解决你的问题,请参考以下文章

2020-03-01emacs 中使用LISP

如何在 Emacs Lisp 中进行闭包?

用于 Emacs Lisp 的 REPL

请教,关于emacs写lisp自动补全的设置

Emacs Lisp 和 Common Lisp 之间的主要区别是啥? [关闭]

Emacs Lisp 中 setq 和 setq-default 的区别