首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >是否有一种在定义后使函数var动态的方法?

是否有一种在定义后使函数var动态的方法?
EN

Stack Overflow用户
提问于 2013-12-06 04:26:17
回答 3查看 122关注 0票数 2

发布更新,使其与事件的进程相关(反应和消除杂乱)。

非常感谢您的时间和帮助!

在Clojure的某些早期版本中,每个var都可以使用"binding“表单绑定。现在,如果不将非动态变量定义为动态,就会得到“无法动态绑定非动态变量”。

在某些情况下,在定义后使函数var动态可能是有用的(在测试中使用挂起/模拟)。

不要尝试:

代码语言:javascript
运行
复制
(def ^{:dynamic true} log-call #'log-call)

它最终会导致StackOverflowError,因为您正在定义一个调用自己的函数(谢谢您的解释)。

最新问题如下:

所建议的办法似乎行不通。

从绑定表单调用的表单没有定义绑定。

你能帮我找出我错过了什么吗??

以下是更新的代码:

代码语言:javascript
运行
复制
(def all-expenses [{:amount 33.0 :date "today"}
                   {:amount 44.0 :date "yesterday"}])

(defn fetch-all-expenses [])

(defn fetch-expenses-greater-than [threshold]
  (let [all (fetch-all-expenses)]
    ;calling from a nested form does not see the dynamically bound definition!
    (println "2) from fetch-expenses-greater-than:" (fetch-all-expenses))
    all))

(defn wrap [f]
  (fn [& args] (apply f args)))

(def ^{:dynamic true} fetch-all-expenses (wrap fetch-all-expenses))

(binding [fetch-all-expenses (constantly all-expenses)]
  (let [filtered (fetch-expenses-greater-than 15.0)]
    (println "1) from inside binding:" (fetch-all-expenses))));calling from binding form OK!

在repl中执行该程序的结果是:

代码语言:javascript
运行
复制
2) from fetch-expenses-greater-than: nil
1) from inside binding: [{:date today, :amount 33.0} {:date yesterday, :amount 44.0}]
nil

如果我将获取所有费用的定义更改为

代码语言:javascript
运行
复制
(defn ^:dynamic fetch-all-expenses [])

结果如预期:

代码语言:javascript
运行
复制
2) from fetch-expenses-greater-than: [{:date today, :amount 33.0} {:date yesterday, :amount 44.0}]
1) from inside binding: [{:date today, :amount 33.0} {:date yesterday, :amount 44.0}]
nil
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-12-06 21:12:25

--在定义了Var动态之后,它是可能的,但是这对在此更改之前编译的代码没有任何影响(它仍将使用根绑定)。使用with-redefs 在测试期间安装函数的自定义替换。。

之所以这样做,是因为Var是否被标记为动态的,这决定了使用此Var编译代码的方式。如果它不是动态的,这样的代码将直接获得根绑定,从而节省一些工作;如果它是动态的,它将经历一个更复杂的过程,检查是否存在线程本地绑定。

因此,在标记Var保持函数动态后,无法导致已经编译的代码使用与binding一起安装的自定义函数。但是,这些调用仍然要经过Var,它们恰好直接进入根绑定,因此,如果将它们安装为相应的Var的根绑定,则可以使用自定义的替换函数进行测试等等。with-redefs封装了干净地执行此操作所需的所有逻辑。

让我们看看REPL是如何工作的:

代码语言:javascript
运行
复制
;; define a non-dynamic Var:
(def foo 0)

;; this will throw an exception complaining about the attempt
;; to bind a non-dynamic Var:
(binding [foo 1]
  foo)

;; let's define a function using foo;
;; we'll use it further down:
(defn bar []
  foo)

;; now let's mark the Var dynamic:
(.setDynamic #'foo)

;; this will now evaluate to 1:
(binding [foo 1]
  foo)

;; however, this will still return 0:
(binding [foo 1]
  (bar))
票数 1
EN

Stack Overflow用户

发布于 2013-12-06 05:31:53

(def ^{:dynamic true} log-call #'log-call)这条语句说:“创建一个var log-call并将其绑定到var log-call,所以当您尝试使用log-call var时,它将永远引用自己,从而导致StackOverflow异常。

你可以试试这样的东西:

代码语言:javascript
运行
复制
(defn wrap [f]
  (fn [& args] (apply f args)))

(def ^{:dynamic true} log-call (wrap log-call))

(def ^{:dynamic true} fetch-all-expenses (wrap fetch-all-expenses))
票数 1
EN

Stack Overflow用户

发布于 2013-12-06 21:47:25

非常感谢您的回答@MichałMarczyk。这就解释了。

在使var动态之前使用var的代码:

代码语言:javascript
运行
复制
(def foo 0)
(defn bar []
  foo)
(.setDynamic #'foo)
(binding [foo 1]
     ;; prints foo 1 . bar 0
     (println  "foo" foo ". bar" (bar)))

在使var动态之后使用var的代码:

代码语言:javascript
运行
复制
(def foo 0)
(.setDynamic #'foo)
(defn bar []
  foo)
(binding [foo 1]
     ;; prints foo 1 . bar 1
     (println  "foo" foo ". bar" (bar)))

是的!....with,with-redefs,而不是binding,一切都像预期的那样工作。这正是我所需要的。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20415961

复制
相关文章

相似问题

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