具有多个调度值的Clojure多方法

Ash*_*her 6 clojure

我有一个案例,我希望多方法中的几个调度值映射到相同的方法.例如,对于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)

这是一个好主意吗?

bsv*_*gen 5

我宁愿做这样的事情:

(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函数用于其预期目的.


Clo*_*tly 5

实际上,这正是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)。

  • 表现应该不错。调度函数的所有输出内容都会映射到其解析的实际方法(遍历层次结构)的映射。


sbe*_*nsu 2

由于这是一个悬而未决的问题(“这是一个好主意吗?”),我将尝试解决我想到的两个问题:

  1. 效率:由于它会产生相同的代码,因此defmethod对于不同的值键入和对它们使用相同的函数一样高效。

  2. DRY、可读性、代码质量:比用不同的匹配值输入 n 次相同的代码要好。

因此,如果这就是您的函数的行为方式,那么它看起来是一个好主意,但您的函数以这种方式行为的事实可能表明您的模型存在缺陷:

  • 您的数据(调度参数)可能会以更好的方式建模,以反映该行为或
  • 多方法可能做得太多或太少,导致尴尬的调度调用。

在确保我的数据/函数应该如何工作之后,我会使用这样的调度机制。