2012-04-25 47 views
17

我有一些使用多方法的代码,并且理想情况下希望重载函数(在本例中为多功能函数),以便我可以传递更高阶的函数来帮助进行测试。是否有可能超载Clojure多方法的arity?

这里的例子:

(ns multi) 

(defn my-print [m] (println "The colour is" (:colour m))) 

(defmulti which-colour-mm (fn [m f] (:colour m))) 

(defmethod which-colour-mm :blue [m f] (f m)) 
(defmethod which-colour-mm :red [m f] (f m)) 
(defmethod which-colour-mm :default [m f] (println "Default: Neither Blue nor Red")) 

(defn which-colour 
    ([m] (which-colour-mm m my-print)) 
    ([m f] (which-colour-mm m f))) 

(which-colour {:colour :blue :object :ball}) 
(which-colour {:colour :yellow :object :ball}) 
(which-colour {:colour :blue :animal :parrot} (fn [m] (println "The " (:animal m) "is" (:colour m)))) 

所以我DEFN提供的元数超载,但我不知道是否defmethod支持这样的事。 (我猜你不想为每个defmethod声明做。)

这是最合适的(我敢说,惯用)的方法,还是有更好的方法吗?

回答

14

这很好。有一个库的“用户”界面和“类型”界面。他们可能是相同的,但他们不需要。

“用户”界面在您的案例which-colour。 “类型”界面是which-colour-mm(好吧,不是真的,但仅仅是为了争论)。您的库的用户不需要了解multimethod。

另一方面,有人提供一种新颜色 - 比如:purple - 不必关心多层样板。这是他在which-colour处理。

这是一个完全有效的设计!

但当然有一个价格标签:假设你有一种颜色,它具有更多的perfomant方式来做事情......现在,你被锁定在一个可能较慢的界面。

澄清这一点:假设你有一个收藏界面。您提供了一个功能 - conj - 允许用户添加元素到集合中。它的实现是这样的:

(defn conj 
    [coll & elements] 
    (reduce conj1 coll elements)) 

conj1是“类型”接口(例如一个多方法或协议功能):它增加了一个元件到集合中。因此,提供新集合类型的人只需实现添加单个参数的简单情况。自动地,新类型也将支持添加多个元素。

但是现在假设您有一个集合类型,它允许以更快的方式添加多个元素,而不仅仅是添加一个元素。此功能现在不能使用。

因此,您使多方法/协议功能本身功能conj。现在收藏可以使用更快的方式。但是每个实现必须提供多个元素样板。

这是一个权衡和你的决定。没有正确的方法(tm)。两者都可以被认为是惯用的。 (虽然我个人会尽可能经常尝试与第一个一起去。)

YMMV。

编辑:在调度值中没有编码的多元方法的一个例子。

(defmulti which-colour-mm (fn [m & args] (:colour m))) 
(defmethod which-colour-mm :blue 
    ([m] (print m)) 
    ([m f] (f m))) 
+0

我喜欢这一点,并ANKUR的回答,但是这一次使用的元数超载v另一个使用参数计数来匹配调度值。如果你想为每个调度值使用相同的默认函数(并避免重复),而在defmethod级别进行重载,如果你想为每个调度值设置不同的默认值,我想使用defn方法是有意义的。 – 2012-04-26 04:28:17

3

你可以做,使用多方法如下面的例子:

(defmulti which-colour-mm (fn [m & args] [(count args) (:colour m)])) 
(defmethod which-colour-mm [0 :blue] [m] (print m)) 
(defmethod which-colour-mm [1 :blue] [m f] (f m)) 


user=> (which-colour-mm {:colour :blue :object :ball}) 
{:colour :blue, :object :ball}nil 
user=> (which-colour-mm {:colour :blue :object :ball} print) 
{:colour :blue, :object :ball}nil 
2

基本上你可以发送任何东西,无论是类型和参数的个数数必须是consistent..like这样的:

(defn- map-classes [an-object] 
    (let [cmap 
     {1 :thing 
      2 666 
      3 "yada"} 
    the-class (class an-object)] 
    (get cmap an-object the-class))) 

(defn- mk-class [& args] (map #(map-classes %) args)) 
(defmulti play-thing mk-class) 
(defmethod play-thing [:thing] [v] (= 1 v)) 
(defmethod play-thing [666] [v] (= 2 v)) 
(defmethod play-thing ["yada" String] [v x] (str x v)) 

可能性是无穷无尽的