Emacs中的命令有“flet”吗?

Emacs中的命令有“flet”吗?,emacs,elisp,Emacs,Elisp,我想动态地将一个命令重定向到 使用around advice的某些函数,如: (defun f1 (arg) (interactive (list (read-from-minibuffer "F1: "))) (message "f1: %S" arg) arg) (defun f2 (arg) (interactive (list (read-from-minibuffer "F2: "))) (message "f2: %S" arg) arg) ;; Functi

我想动态地将一个命令重定向到 使用around advice的某些函数,如:

(defun f1 (arg)
  (interactive (list (read-from-minibuffer "F1: ")))
  (message "f1: %S" arg)
  arg)
(defun f2 (arg)
  (interactive (list (read-from-minibuffer "F2: ")))
  (message "f2: %S" arg)
  arg)
;; Function that invokes the f1 command
(defun myfunc ()
  (call-interactively 'f1))

;; I want myfunc to invoke f2 instead whenever it would invoke f1
(defadvice myfunc (around f1-to-f2 activate)
  (flet ((f1 (&rest args) (interactive) (call-interactively 'f2)))
    ad-do-it))

(myfunc)
但是,这会产生一个错误
(错误的类型参数commandp f1)
, 表明当
flet
重新定义
f1
函数时,它没有 处理交互式表单并将其视为命令,因此它不能 可由
交互调用调用

是否有一种
flet
的变体可以以这种方式用于命令

(以下是我想做的实际重新定义:)

(编辑:
cl-letf
宏可以在现代emacs中本机完成此操作。下面的答案对于旧版本可能仍然有用。)

如果以前没有,现在有了:

(require 'cl)
(require 'cl-lib)
(defmacro command-let (bindings &rest body)
  "Like `flet', but works for interactive commands.

In addition to the standard `(FUNC ARGLIST BODY...)' syntax from
`flet', this also supports `(FUNC NEW-FUNC)' as a shorthand for
remapping command FUNC to another command NEW-FUNC, like this:

  (defun FUNC (&rest ignored)
    (interactive)
    (call-interactively NEW-FUNC))

\(fn ((FUNC ARGLIST BODY...) ...) FORM...)"
  (declare (indent 1))
  (cl-loop for binding in bindings
           collect (list (car binding) nil) into empty-bindings
           collect (if (symbolp (cadr binding))
                       ;; Remap one command to another
                       `(defun ,(car binding) (&rest args)
                          (interactive)
                          (call-interactively ',(cadr binding)))
                     ;; Define command on the fly
                     (cons 'defun binding))
           into defun-forms
           finally return
           `(flet (,@empty-bindings)
              ,@defun-forms
              ,@body)))
在行动中:

(defadvice myfunc (around f1-to-f2 activate)
  (command-let ((f1 f2))
    ad-do-it))
(myfunc)

该代码现在根据需要使用交互式调用调用调用
f2
命令。

您在flet中遇到了一个愚蠢的错误:flet的宏扩展将有:
(lambda(&rest args)(progn(interactive)(交互式调用“f2”)
。注意这里添加的伪
progn
,它“隐藏”了
交互

要获得更多控制(同时避免cl.el),您可以执行以下操作:

(defadvice myfunc (around f1-to-f2 activate)
  (cl-letf (((symbol-function 'f1)
             (lambda (&rest args)
               (interactive) (call-interactively 'f2))))
    ad-do-it))

(需要'cl-lib)
是不够的,因为
flet
不是由cl-lib提供的(而是由cl.el提供的)。感谢您的提醒
cl-lib
用于循环宏,我忘记了
flet
不是内置的。而且,我总是忘记哪个
flet
变体使用动态绑定,哪个使用词汇绑定。显然我需要动态绑定。@ryanthonson:是的,它相当混乱。它们背后的故事很有帮助,因为:
cl-lib
较新,因此
cl-flet
flet
更新,所以
cl-flet
是词法的“自然”(因为词法范围界定在Emacs中较新)。至于
letf
f
来自
setf
,而不是来自“let function”,因此它实际上只是一种“设置…恢复”的方式,因此是动态范围。
(defadvice myfunc (around f1-to-f2 activate)
  (cl-letf (((symbol-function 'f1)
             (lambda (&rest args)
               (interactive) (call-interactively 'f2))))
    ad-do-it))