【问题标题】:Is it possible to overload Clojure multi-methods on arity?是否可以在 arity 上重载 Clojure 多方法?
【发布时间】:2012-04-25 10:16:48
【问题描述】:

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

示例如下:

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

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

【问题讨论】:

    标签: clojure multimethod


    【解决方案1】:

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

    “用户”界面在您的情况下是 which-colour。 “类型”接口是which-colour-mm(好吧,不是真的,只是为了论证)。您的库的用户不需要了解多方法。

    另一方面,提供新颜色的人——比如:purple——不必关心多参数样板。这是在which-colour 中为他处理的。

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

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

    稍微澄清一下:假设您有一个集合接口。您提供了一个函数 - 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)))
    

    【讨论】:

    • 我喜欢这个和 Ankur 的回答,但是这个使用arity 重载,而另一个使用参数计数来匹配调度值。我想如果你想为每个调度值使用相同的默认函数(并避免重复)而不是在 defmethod 级别重载,那么使用 defn 方法是有意义的,如果你想为每个调度值设置不同的默认值。
    【解决方案2】:

    您可以使用多种方法来做到这一点,如下例所示:

    (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
    

    【讨论】:

      【解决方案3】:

      基本上你可以派发任何东西,无论是类型还是参数的数量都不必是一致的..就像这样:

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

      无限可能

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多