Clojure多方法能否根据参数个数进行重载?

20

我有一些使用多方法的代码,并且想要重载函数(在这种情况下,是多函数),以便我可以传递一个高阶函数来帮助测试,例如。

以下是示例:

(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声明都这么做。)

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

3个回答

16

这是完全正常的。一个库有"用户"界面和"类型"界面,它们可能相同,但也可能不同。

在你的情况下,"用户"界面是which-colour。"类型"界面是which-colour-mm(好吧,实际上不是,但为了论证而已)。您的库用户不需要知道多方法。

另一方面,提供新颜色的人 - 比如:purple - 不需要关心多元性样板文件。这在which-color中已经处理好了。

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

当然,这也有代价:假设您有了某种更高效的处理方式...现在,您只能使用潜在更慢的接口。

为了澄清这一点:假设您有一个集合接口。您提供一个函数conj,让用户向集合添加元素。它的实现方式如下:

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

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

但是现在假设你有一种集合类型,它允许比依次添加一个元素更快地添加多个元素。现在这种能力无法使用。

所以你将多方法/协议函数作为函数conj本身。现在集合可以使用更快的方式。但是每个实现都必须提供多个元素的样板文件。

这是一种权衡,由您自己决定。没有“正确的”方式。两者都可以被认为是惯用语(尽管我个人尽可能选择第一种方式)。

YMMV。

编辑:多元方法的示例,而不需要在分派值中进行编码。

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

我喜欢这个和Ankur的答案,但是这个使用了arity重载而另一个使用参数计数来匹配分派值。如果你想要每个分派值都有相同的默认函数(并避免重复),那么使用defn方法可能是有意义的,而在defmethod级别上进行重载则可以为每个分派值设置不同的默认值。 - Andrew Whitehouse

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

基本上你可以在任何东西上进行调度,无论参数的类型和数量是否一致.... 就像这样:

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

无限可能


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接