如何编写透明的“传递”函数包装器?


10

我所说的“透明的“传递”函数包装器”是一个函数,我们称它为“函数”,它wrapper通过将其所有参数传递给其他函数(称为它)来返回结果wrappee

在Emacs Lisp中如何完成?

注意:理想的wrapper功能与功能的签名无关wrappee。即它不知道的参数的数量,位置,名称等wrappee;它只是将其所有参数传递给wrappee,就像wrappee最初调用的那样。(不过,您无需弄乱调用堆栈,即可将调用替换wrapperwrappee。)

我对我的问题发表了部分答案:

(defun wrapper (&rest args) (apply 'wrappee args))

仅当工作wrappee没有互动。显然,交互函数获取其参数的方式代表了与该(&rest args)咒语所涵盖内容不同的“渠道” 。我还是需要的,因此,是一个equally- wrappee的的-agnostic对应(&rest args)于其中的情况下签名wrappee是一个互动的功能。

(此问题是由先前问题中描述的问题引起的。)


如果需要进一步澄清我要的内容,下面是几个示例,显示了我所追求的Python和JavaScript等效项。

在Python中,实现这种包装器的几种标准方法如下所示:

def wrapper(*args, **kwargs):
    return wrappee(*args, **kwargs)

# or

wrapper = lambda *args, **kwargs: wrappee(*args, **kwargs)

(此处*args代表“所有位置参数”,**kwargs代表“所有关键字参数”。)

相当于JavaScript的是这样的:

function wrapper () { return wrappee.apply(this, arguments); }

// or

wrapper = function () { return wrappee.apply(this, arguments); }

作为记录,我不同意这个问题是如何将mapcar应用于具有多个参数的函数的重复。我无所适从地解释了为什么,因为这两个问题对我而言显然如此不同。就像被问到“解释为什么苹果不应该被认为等同于橙子”。单纯的问题是如此疯狂,以至于有人怀疑有人会提出一个满足要求的人的答案。


您是否考虑过使用建议/建议?
wasamasa

@wasamasa:不,而且,我看不到建议/建议如何应用于该问题。无论如何,我发现这些advice东西有足够的问题,以至于我宁愿不要这样做。实际上,这个问题的动机是试图为我所建议的功能解决其他棘手的问题……
kjo

1
@wasamasa:咨询提出了同样的问题。您可以告诉它如何处理任何args,但要使其具有交互性,您需要指定如何提供参数。IOW,您需要提供interactive规格。
提请

1
我的意思是建议原始的交互功能在此之前和之后执行您想做的任何事情,这样您就不必担心交互规范。
wasamasa'1

2
@wasamasa:是的,但是不一样。建议始终针对特定功能,无论是否互动。而且,如果这是一个命令,那么就没有问题-它的交互行为是为建议的命令继承的(除非该建议重新定义了交互行为)。这个问题是关于任意功能/命令的,而不是特定的。
提请

Answers:


11

当然,可以包括interactive说明书。我们在这里与elisp交易!(Lisp是最重要的构造是列表的语言。可调用形式只是列表。因此,您可以在喜欢后构造它们。)

应用程序:您想自动为某些功能添加一些功能。扩展功能应使用新名称,因此defadvice不适用。

首先,一个完全符合您目的的版本。我们使用所有必需的信息来fset设置符号的函数单元格(),并添加其他内容。wrapperwrappee

它适用于两个wrappee定义。第一个版本wrappee是交互式的,第二个版本不是交互式的。

(defun wrappee (num str)
  "Nontrivial wrappee."
  (interactive "nNumber:\nsString:")
  (message "The number is %d.\nThe string is \"%s\"." num str))

(defun wrappee (num str)
  "Noninteractive wrappee."
  (message "The number is %d.\nThe string is \"%s\"." num str))

(fset 'wrapper (list 'lambda
             '(&rest args)
             (concat (documentation 'wrappee t) "\n Wrapper does something more.")
             (interactive-form 'wrappee)
             '(prog1 (apply 'wrappee args)
            (message "Wrapper does something more."))))

但是,定义构造扩展功能的宏更为方便。这样我们甚至可以在以后指定函数名称。(适用于自动版本。)

执行以下代码后,您可以wrapper-interactive进行交互式和wrapper-non-interactive非交互式调用。

(defmacro make-wrapper (wrappee wrapper)
  "Create a WRAPPER (a symbol) for WRAPPEE (also a symbol)."
  (let ((arglist (make-symbol "arglist")))
  `(defun ,wrapper (&rest ,arglist)
     ,(concat (documentation wrappee) "\n But I do something more.")
     ,(interactive-form wrappee)
     (prog1 (apply (quote ,wrappee) ,arglist)
       (message "Wrapper %S does something more." (quote ,wrapper))))))

(defun wrappee-interactive (num str)
  "That is the doc string of wrappee-interactive."
  (interactive "nNumber:\nsString:")
  (message "The number is %d.\nThe string is \"%s\"." num str))

(defun wrappee-non-interactive (format &rest arglist)
  "That is the doc string of wrappee-non-interactive."
  (apply 'message format arglist))

(make-wrapper wrappee-interactive wrapper-interactive)
(make-wrapper wrappee-non-interactive wrapper-non-interactive)
;; test of the non-interactive part:
(wrapper-non-interactive "Number: %d, String: %s" 1 "test")

注意,到目前为止,我还没有找到一种方法来传递声明形式,但是那也应该可行。


2
嗯,有人否决了这个答案。我并不真的在乎分数,但是我在乎的是拒绝答案的原因。如果您不赞成,也请发表评论!这将使我有机会改善答案。
Tobias'1

我不确定,但这必定会使任何使用该软件包阅读代码的人进入WTF。在大多数情况下,更明智的选择是处理该问题并编写一个手动执行换行的函数(无论是应用还是重写交互式规范的某些部分
。– wasamasa

2
@wasamasa我部分同意。但是,在某些情况下必须使用自动仪表。一个例子是edebug。此外,有些功能的interactive-规格要比功能主体大得多。在这种情况下,interactive规范的重写可能非常繁琐。问题和答案解决了必需的原则。
Tobias'1

1
就我个人而言,我发现这个答案非常有启发性,不仅在问题的范围方面,而且在它显示宏的自然应用以及如何从defun到宏的过程中都表现得淋漓尽致。谢谢!
gsl

11

我必须在中解决一个非常类似的问题nadvice.el,因此这是一个解决方案(使用了nadvice.el的一些代码):

(defun wrapper (&rest args)
  (interactive (advice-eval-interactive-spec
                (cadr (interactive-form #'wrappee))))
  (apply #'wrappee args))

与到目前为止发布的其他解决方案相比,该解决方案的优势在于,如果wrappee使用其他交互式规范进行了重新定义(例如,它将不会继续使用旧规范),则可以正常工作。

当然,如果希望包装器真正透明,则可以更简单地做到这一点:

(defalias 'wrapper #'wrappee)

这是唯一允许定义包装程序的答案,该包装程序可以在运行时找到要包装的内容。例如,我想添加一个快捷方式,该快捷方式执行由在运行时查找的某些命令定义的操作。advice-eval-interactive-spec按照此处的建议使用,我可以构建与该动态包装器相对应的交互式规范。
伊戈尔·布卡诺夫

是否有可能使called-interactively-p返回twrappee?没有funcall-interactively但没有apply-interactively
clemera

1
@compunaut:当然,您可以(apply #'funcall-interactively #'wrappee args)根据需要进行操作。但是,只有在以交互方式调用函数时,才应这样做,例如(apply (if (called-interactively-p 'any) #'funcall-interactively #'funcall) #'wrappee args)
Stefan's

哈,谢谢!不知何故,我无法思考。
clemera

1

编辑:Tobias的答案比这更好,因为它获得了包装函数的精确交互形式和文档字符串。


结合亚伦·哈里斯(Aaron Harris)和kjo的答案,您可以使用类似以下的方法:

(defmacro my-make-wrapper (fn &optional name)
  "Return a wrapper function for FN defined as symbol NAME."
  `(defalias ',(or (eval name)
                   (intern (concat "my-" (symbol-name (eval fn)) "-wrapper")))
     (lambda (&rest args)
       ,(format "Generic wrapper for %s."
                (if (symbolp (eval fn))
                    (concat "`" (symbol-name (eval fn)) "'")
                  fn))
       (interactive)
       (if (called-interactively-p 'any)
           (call-interactively ,fn)
         (apply ,fn args)))))

用法:

(my-make-wrapper 'find-file 'wrapper-func)

使用以下任一方法调用包装器:

(wrapper-func "~/.emacs.d/init.el")

M-x wrapper-func

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.