是否有可能在arity上重载Clojure多方法?

时间:2012-04-25 10:16:48

标签: clojure multimethod

我有一些使用多方法的代码,理想情况下会重载函数(在本例中为多功能),以便我可以传递更高阶的函数来帮助测试,例如。

以下是示例:

(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提供了arity重载,但我想知道defmethod是否支持这样的事情。 (我猜你不会为每个defmethod声明做这件事。)

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

3 个答案:

答案 0 :(得分:14)

这很好。存在库的“用户”界面和“类型”界面。它们可能是相同的,但它们不必相同。

“用户”界面在您的案例which-colour中。 “type”接口是which-colour-mm(好吧,不是真的,但仅仅是为了参数)。您图书馆的用户无需了解多方法。

另一方面,提供新颜色的人 - 比如说:purple - 并不需要关心多种模板。这是在which-colour中为他处理的。

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

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

稍微澄清一点:假设你有一个收集界面。您提供了一个函数 - conj - 允许用户向集合中添加元素。它实现如下:

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

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

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

因此,您将multimethod / protocol函数设置为函数conj本身。现在集合可以使用更快的方式。但每个实现都必须提供多元素样板。

这是一个权衡因素,取决于您的决定。没有正确的方式(tm)。两者都可以被认为是惯用的。 (虽然我个人会尝试尽可能多地使用第一个。)

因人而异。

编辑:在调度值中没有编码的多个arity方法示例。

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

答案 1 :(得分:3)

你可以使用multimethods来做到这一点,如下例所示:

(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 :(得分:2)

基本上你可以派遣任何东西,args的类型和数量都不一致..就像这样:

(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))

可能性无穷无尽

相关问题