在 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的主要内容,如果未能解决你的问题,请参考以下文章