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 宣言ごとに実行したくないと思います。)

これは最も適切な (あえて言いますが、慣用的な) アプローチですか、それとももっと良い方法がありますか?

4

3 に答える 3

15

これはまったく問題ありません。ライブラリには「ユーザー」インターフェースと「タイプ」インターフェースがあります。それらは同一である可能性がありますが、必ずしもそうである必要はありません。

「ユーザー」インターフェースはあなたの場合which-colourです。「タイプ」インターフェースはwhich-colour-mm(わかりました、実際にはそうではありませんが、引数のためだけです)。ライブラリのユーザーは、マルチメソッドについて知る必要はありません。

一方、新しい色を提供する人は、たとえば:purple、マルチアリティのボイラープレートを気にする必要はありません。これは で処理されwhich-colourます。

これは完全に有効な設計です。

しかし、もちろん値札があります: あなたが色を持っていて、物事を行うためのよりパフォーマンスの高い方法を持っているとしましょう... 今、あなたは可能性のあるより遅いインターフェースに閉じ込められています.

これを少し明確にするために、コレクション インターフェイスがあるとします。関数を提供します - conj- ユーザーが要素をコレクションに追加できるようにします。次のように実装されます。

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

conj1「タイプ」インターフェース (例えば、マルチメソッドまたはプロトコル関数) です: コレクションに 1 つの要素を追加します。したがって、新しいコレクション型を提供する人は、単一の引数を追加するという単純なケースを実装するだけで済みます。そして自動的に、新しい型は複数の要素の追加もサポートします。

しかし、次から次へと要素を追加するよりも、複数の要素をより迅速に追加できるコレクション型があるとします。この機能は現在使用できません。

したがって、マルチメソッド/プロトコル関数を関数conj自​​体にします。これで、コレクションはより高速な方法を使用できるようになりました。ただし、各実装では、複数の要素のボイラープレートを提供する必要があります。

これはトレードオフであり、あなたの決定次第です。Right Way(tm)はありません。どちらも慣用句と見なすことができます。(個人的には、できるだけ最初のものを使用しようとしますが。)

YMMV。

編集:ディスパッチ値をコーディングしないマルチアリティメソッドの例。

(defmulti which-colour-mm (fn [m & args] (:colour m)))
(defmethod which-colour-mm :blue
  ([m] (print m))
  ([m f] (f m)))
于 2012-04-25T12:17:20.273 に答える
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
于 2012-04-25T12:25:22.103 に答える
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))

可能性は無限大

于 2012-05-22T23:57:41.373 に答える