我在REPL中编写了一个多方法,函数可以很好地重新定义,但是如果我重新定义多方法的调度函数,它似乎不会使用新改进的函数:
;; simple fn to resolve defmethod to call, hardcoded to :do-it
(defn resolve-it [] :do-it)
(resolve-it) ;; :do-it, as expected
(defmulti do-something resolve-it)
(defmethod do-something :do-it [] (println "do-it"))
(defmethod do-something :oh-no [] (println "oh-no"))
(do-something) ;; "do-it", as expected
;; now change resolve-it
(defn resolve-it [] :oh-no)
(resolve-it) ;; :oh-no, as expected
(do-something) ;; "do-it", not as expected
(do-something) ;; "do-it", not expected
如何让mult imethod反映调度函数resolve-it
的变化?
答案 0 :(得分:8)
有一种简单的技术可以重新定义多方法的调度功能。我们的想法是将保存调度函数的var传递给defmulti
,而不是函数本身。请注意#'resolve-it
中的defmulti
,而不只是resolve-it
。因此,var在运行时被解除引用,而不仅仅是在编译时。
(defn resolve-it [] :do-it)
(resolve-it) ;; :do-it, as expected
(defmulti do-something #'resolve-it)
(defmethod do-something :do-it [] (println "do-it"))
(defmethod do-something :oh-no [] (println "oh-no"))
(do-something) ;; "do-it", as expected
;; now change resolve-it
(defn resolve-it [] :oh-no)
(resolve-it) ;; :oh-no, as expected
(do-something) ;; "oh-no", expected!!
答案 1 :(得分:4)
根据clojuredocs exmaple var arr = [
{"123": "valueA"},
{"456": "valueB"}
];
const id = "123";
let value;
arr.some(obj => {
if (obj[id] || obj[id] === 0) value = obj[id];
});
console.log(value);
,我们不允许您重新定义它。您必须从命名空间(defmulti
)取消映射do-something
:
ns
并像以前一样重新分配:
(ns-unmap *ns* 'do-something)
答案 2 :(得分:1)
看起来defmulti
正在缓存调度功能。以下是代码的修改版本,用于说明问题:
;; simple fn to resolve defmethod to call, hardcoded to :do-it
(defn who-is-it [person] (:name person))
(spyx (who-is-it {:name :joe}))
(defmulti do-something who-is-it)
(defmethod do-something :homer [person] :doh)
(defmethod do-something :bill [person] :oh-no)
(defmethod do-something :ted [person] :excellent)
(spyx (do-something {:name :homer}))
(spyx (do-something {:name :bill}))
;; now change who-is-it
(defn who-is-it [arg] :ted)
(spyx (who-is-it :wilma)) ;; expected result = :excellent
(spyx (do-something {:name :betty}))
结果:
:reloading (tst.clj.core)
(who-is-it {:name :joe}) => :joe
(do-something {:name :homer}) => :doh
(do-something {:name :bill}) => :oh-no
(who-is-it :wilma) => :ted
:error-while-loading tst.clj.core
Error refreshing environment: java.lang.IllegalArgumentException: No method in multimethod 'do-something' for dispatch value: :betty, compiling:(tst/clj/core.clj:22:27)
看起来您可能需要重新初始化REPL以重新定义调度fn。即使重复所有内容也没有为我覆盖do-something
:
(defmulti do-something who-is-it)
(defmethod do-something :homer [person] :doh)
(defmethod do-something :bill [person] :oh-no)
(defmethod do-something :ted [person] :excellent)
(spyx (do-something {:name :betty})) ;=> ***same error ***
Error refreshing environment: java.lang.IllegalArgumentException: No method in multimethod 'do-something' for dispatch value: :betty, compiling:(tst/clj/core.clj:30:1)
在新会话中,我们看到了预期的行为:
;; simple fn to resolve defmethod to call, hardcoded to :do-it
(defn who-is-it [person] (:name person))
(spyx (who-is-it {:name :joe}))
;; now change who-is-it
(defn who-is-it [arg] :ted)
(spyx (who-is-it :wilma)) ;; expected result = :ted
; (spyx (do-something {:name :betty}))
(defmulti do-something who-is-it)
(defmethod do-something :homer [person] :doh)
(defmethod do-something :bill [person] :oh-no)
(defmethod do-something :ted [person] :excellent)
(dotest
(spyx (do-something {:name :betty})))
(do-something {:name :betty}) => :excellent ; *** as expected ***
我尝试了Rumid描述的ns-unmap
技术,它也有效。我注意到您必须重新发布 defmulti
和 所有defmethod
语句:
(ns-unmap *ns* 'do-something) ; be sure to remember the quote
(defmulti do-something who-is-it)
(defmethod do-something :homer [person] :doh)
(defmethod do-something :bill [person] :oh-no)
(defmethod do-something :ted [person] :excellent)
(dotest
(newline)
(spyx (do-something {:name :betty}))) ;=> :excellent