我有一个案例,我希望多方法中的几个调度值映射到相同的方法.例如,对于1的调度值,我希望它调用method-a,对于调度值2,3或4,我希望它调用method-b.
经过一些谷歌搜索,我最终编写了以下宏:
(defmacro defmethod-dispatch-seq [mult-fn dispatch-values & body]
`(do (map
(fn [x#] (defmethod ~mult-fn x# ~@body))
~dispatch-values)))
Run Code Online (Sandbox Code Playgroud)
然后你可以像这样使用它:
(defmulti f identity)
(defmethod f 1 [x] (method-a x))
(defmethod-dispatch-seq f [2 3 4] [x] (method-b x))
Run Code Online (Sandbox Code Playgroud)
允许您拨打以下电话:
(f 1) => (method-a 1)
(f 2) => (method-b 2)
(f 3) => (method-b 3)
(f 4) => (method-b 4)
Run Code Online (Sandbox Code Playgroud)
这是一个好主意吗?
我宁愿做这样的事情:
(defn dispatch-function
[value]
(if (= 1 value) :case-a :case-b))
(defmulti f dispatch-function)
(defmethod f :case-a
[x]
:doing-something)
(defmethod f :case-b
[x]
:doing-something-else)
Run Code Online (Sandbox Code Playgroud)
这样就可以避免使用宏,并将dispatch函数用于其预期目的.
实际上,这正是Clojure中的层次结构的目的。该defmulti需要:hierarchy,可以建立参数x是AY关系。
唯一的问题是层次结构不适用于数字。您不能说数字0是“ :a例如”。但是,希望您真的只是为了简单起见选择数字并提出问题,并且在实际情况中可能有关键字,您可以执行以下操作:
(def hh (-> (make-hierarchy)
(derive :0 :a)
(derive :1 :a)
(derive :2 :b)
(derive :3 :b)
atom))
(isa? @hh :3 :b) ;; => true
(defmulti mm
"Demo of using hierarchy"
(comp keyword str)
:hierarchy hh)
(defmethod mm :b [orig] {:b orig})
(defmethod mm :a [orig] {:a orig})
(defmethod mm :default [orig] :oopsie)
(mm 2) ;; => {:b 2}
(mm 1) ;; => {:a 1}
(mm 4) ;; => :oopsie
;; Cool thing is we can steer this at RUNTIME!
(swap! hh derive :4 :b)
(mm 4) ;; => {:b 4}
;; So we can keep the data close to the definition:
(mapv #(swap! hh derive % :c) [:5 :6 :7])
(defmethod mm :c [orig] {:c orig})
(mm 6) ;; => {:c 6}
Run Code Online (Sandbox Code Playgroud)
笔记:
我使用atom而不是,(var hh)因为它与clojurescript更好地兼容(cljs在生产中不喜欢vars)。
表现应该不错。调度函数的所有输出内容都会映射到其解析的实际方法(遍历层次结构)的映射。
由于这是一个悬而未决的问题(“这是一个好主意吗?”),我将尝试解决我想到的两个问题:
效率:由于它会产生相同的代码,因此defmethod对于不同的值键入和对它们使用相同的函数一样高效。
DRY、可读性、代码质量:比用不同的匹配值输入 n 次相同的代码要好。
因此,如果这就是您的函数的行为方式,那么它看起来是一个好主意,但您的函数以这种方式行为的事实可能表明您的模型存在缺陷:
在确保我的数据/函数应该如何工作之后,我会使用这样的调度机制。
| 归档时间: |
|
| 查看次数: |
1824 次 |
| 最近记录: |