























cl-flet 的行为和 let 一样,只不过它绑定的是函数而非变量。定义只在 cl-flet 的 body 内部可见:
(cl-flet ((double (n) (* n 2)))
(double 21))
42
关键陷阱在这里: cl-flet 的绑定是词法作用域的,也就是说,被调用的其他函数看不到这个覆盖:
(defun my-helper () (+ 1 2)) (defun my-caller () (my-helper)) (cl-flet ((my-helper () 999)) (my-caller))
3
my-caller 内部调用 my-helper 时,看到的仍然是全局定义的那个版本,返回 3 而不是 999。
为什么会这样?因为 cl-flet 用的是词法作用域:它定义的 my-helper 只在 cl-flet 的 body 源代码文本里可见。编译器在编译 my-caller 时, my-caller 的源代码里写的是 (my-helper) ,编译器在它所在的作用域里找到了全局的那个 my-helper ,于是把这个引用固定下来了。后面 cl-flet 再怎么定义局部的 my-helper ,都跟已经编译好的 my-caller 没关系了。
这和旧 flet 的行为完全相反。旧 flet 直接去改全局 function cell 里的内容,所有函数在运行时查找 my-helper 时都会找到那个被替换的版本,不管调用者是谁、在哪里定义的。
同理, cl-flet 定义的函数在自己的定义体内也看不到自己:
(cl-flet ((fact (n) (if (<= n 1) 1 (* n (fact (- n 1)))))) (fact 5))
Symbol's function definition is void: fact
cl-flet 还有一个变体 cl-flet* ,行为就像 let* 之于 let :后面的绑定可以引用前面的。
日常使用中, cl-flet 是最常用的那个。当你只需要一个简单的局部 helper、不需要递归时,用它就对了。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。