据我所知,两者都用于临时更改函数的值。除了cl-flet
是一个函数而cl-letf
是一个宏,你什么时候使用它们?
答案 0 :(得分:9)
绑定可以是递归的
如果函数定义按名称调用自身,将调用哪个函数? (比较cl-flet
与cl-labels
行为)。
范围是词汇......在封闭中捕捉它们......
阅读词汇绑定/范围与动态绑定/范围。
cl-letf
可用于设置动态绑定函数值,对某些FUNC使用(symbol-function 'FUNC)
的PLACE。这类似于已弃用的flet
。
可以指定任何支持的PLACE,但cl-letf
不仅 用于功能绑定。
你什么时候使用它们?
当您想要临时定义(或覆盖)某个功能时。对于任何给定的用例,您需要的范围规则将决定您将使用哪个选项。
(cl-flet ((FUNC ARGLIST BODY...) ...) FORM...)
FUNC仅对FORM中的代码可见。
(cl-labels ((FUNC ARGLIST BODY...) ...) FORM...)
对于FORM中的代码以及FUNC自己的身体中的代码都可以看到FUNC。
(cl-letf (((symbol-function 'FUNC) VALUE) ...) BODY...)
在BODY完成评估之前,绝对可以看到FUNC。
一些(相当人为的)例子......
在第一个例子中,我们定义的临时函数是递归 - 它调用自身 - 因此我们使用cl-labels
:
(n.b。这不是一个强有力的因子实现;它仅用于演示目的。)
(defun my-factorial (number)
"Show the factorial of the argument."
(interactive "nFactorial of: ")
(cl-labels ((factorial (n) (if (eq n 1)
1
(* n (factorial (1- n))))))
(message "Factorial of %d is %d" number (factorial number))))
如果您将cl-labels
更改为cl-flet
,则会在评估内部(factorial (1- n))
时出现错误,因为在我们的临时函数中,没有函数factorial
已知。
如果您要定义全局factorial
函数,该函数无条件地返回值1
:
(defun factorial (n) 1)
然后factorial
定义的cl-flet
函数会在调用factorial
时看到 ,而my-factorial
会计算(* n 1)
作为任何参数n
的值。
如果不需要递归,cl-flet
可以使用:
(defun my-square (number)
"Show the square of the argument."
(interactive "nSquare of: ")
(cl-flet ((square (n) (* n n)))
(message "Square of %d is %d" number (square number))))
cl-labels
和cl-flet
都提供了词法范围的函数,只显示 到在这些宏调用的正文中编写的代码;特别是不到我们可能调用的任何其他函数的代码。
如果你正在定义一个辅助函数,例如上面的例子中,词法作用域可能就是你想要的,因为你有合理的机会只能在宏体内调用你的助手。
但是,如果您尝试暂时覆盖现有功能,那么您很可能需要使用您正在呼叫的功能来查看覆盖。在这种情况下,您需要覆盖以具有动态范围。
过去flet
是为临时函数提供动态范围的方法,但现在不推荐使用flet
,而赞成将cl-letf
与'地点' (symbol-function 'FUNC)
在以下示例中,将覆盖乘法函数,动态范围意味着my-square
和my-factorial
将查看并使用我们的临时乘法定义。
(defun my-bad-square ()
"Maths gone wrong."
(interactive)
(cl-letf (((symbol-function '*) '+))
(call-interactively 'my-square)))
(defun my-bad-factorial ()
"More maths gone wrong."
(interactive)
(cl-letf (((symbol-function '*)
(lambda (x y) (- x y))))
(call-interactively 'my-factorial)))