首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何优雅地修改函数的定义

如何优雅地修改函数的定义
EN

Stack Overflow用户
提问于 2013-04-10 10:56:01
回答 3查看 713关注 0票数 8

假设在库中定义了一个示例函数(此问题的前提条件是此库中的所有定义都不能修改,类似于“只读”):

代码语言:javascript
复制
(defun sample ()
  (foo)
  (bar)
  (baz))

我想使用这个库,但是函数sample不能满足我的请求,我想要的是:

代码语言:javascript
复制
(defun sample ()
  (foo)
  (when condition
    (bar))
  (baz))

有人告诉我使用defadvice,但我注意到defadvice只能在sample调用之前或之后插入代码,例如:

代码语言:javascript
复制
(before-advice ...)
(sample)
(after-advice ...)

它不能修改sample本身的定义。那么,我如何才能优雅地实现这一点呢?我应该自己重写一个名为my-samplesample2sample

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-04-10 12:26:41

sds的答案是有效的,除了您可能只想在sample执行时向bar提供建议,因此您还需要向sample提供建议,以便激活和取消激活bar的建议。我的with-temporary-advice宏促进了这一点:

代码语言:javascript
复制
(defmacro with-temporary-advice (function class name &rest body)
  "Enable the specified advice, evaluate BODY, then disable the advice."
  `(unwind-protect
       (progn
         (ad-enable-advice ,function ,class ,name)
         (ad-activate ,function)
         ,@body)
     (ad-disable-advice ,function ,class ,name)
     (ad-activate ,function)))

(defadvice bar (around my-conditional-bar disable)
  ;; This advice disabled by default, and enabled dynamically.
  (when condition
    ad-do-it))

(defadvice sample (around my-sample-advice activate)
  "Make execution of `bar' conditional when running `sample'."
  (with-temporary-advice 'bar 'around 'my-conditional-bar
   ad-do-it))

请注意,如果在执行sample时还以其他方式调用bar,则建议也适用于这些调用,因此如果有可能,您应该考虑到这一点。

或者,在需要时,您可能更喜欢使用flet来重新定义bar。当然,这受到与第一个解决方案相同的警告。

代码语言:javascript
复制
(defadvice sample (around my-sample-advice activate)
  "Make execution of `bar' conditional when running `sample'."
  (if condition
      ad-do-it
    (flet ((bar () nil))
      ad-do-it)))

这读起来简单多了,但由于我不明白的原因,从Emacs24.3开始,flet不再受欢迎。它的文档字符串建议使用cl-flet,但由于cl-flet使用词法绑定,这实际上并不起作用。据我所知,听起来flet实际上并没有消失,但目前的建议似乎是使用建议。

还要注意,如果在bar中,不想要的行为依赖于某个变量,那么更可取的做法是对该变量使用let绑定,而不是对函数使用flet绑定。

编辑:

当然,这些方法确实让我们更难看到正在发生的事情。根据具体情况,最好是简单地重新定义sample函数来执行您想要的操作(或者按照您的建议编写一个my-sample函数来代替它进行调用)。

票数 5
EN

Stack Overflow用户

发布于 2013-04-10 21:53:41

其他人已经提供了很好的答案,但由于一些人抱怨flet的耻辱,我将展示我将使用的:

代码语言:javascript
复制
(defvar my-inhibit-bar nil)
(defadvice bar (around my-condition activate)
  (unless my-inhibit-bar ad-do-it))
(defadvice sample (around my-condition activate)
  (let ((my-inhibit-bar (not condition)))
    ad-do-it))

快看,妈妈!没有flet和丑陋的激活/停用!当你使用C-h f bar的时候,它会清楚地告诉你,这比你看到的要多得多。另外,我实际上会使用新的advice-add

代码语言:javascript
复制
(defvar my-inhibit-bar nil)
(defun my-bar-advice (doit &rest args)
  (unless my-inhibit-bar (apply doit args)))
(advice-add :around 'bar #'my-bar-advice)
(defun my-sample-advice (doit &rest args)
  (let ((my-inhibit-bar (not condition)))
    (apply doit args)))
(advice-add :around 'sample #'my-sample-advice)
票数 4
EN

Stack Overflow用户

发布于 2013-04-10 12:07:18

您应该使用around建议来建议function bar

代码语言:javascript
复制
(defadvice bar (around my-condition)
  (when condition
    ad-do-it))
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15916124

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档