2017-02-13 2 views
1

既存の関数と同じ名前のメソッドを宣言し、プロトコル/レコードのメソッドに動的にディスパッチするdefprotocol(およびそれを実装するにはdefrecord)を書くにはどうすればよいですか? iff私はそれをプロトコル/レコードのインスタンスで呼びますが、それ以外の場合は既存の関数にディスパッチしますか?既存の関数でClojureのdefprotocolをうまく動かす(多態的に)

例えば、私は基本的な算術演算をサポートしているジオメトリヘルパー(わずかにそれを維持するために、この例では乗算)作成したい:私はすでに、一部の予感プッシュバックを取得しています。この時点で

(defprotocol SizeOps 
    (* [this factor] "Multiply each dimension by factor and return a new Size")) 

をコンパイラ:

警告:プロトコル# 'ユーザー/ SizeOpsが上書きされる機能*
WARNING:*すでに言及へ:#' clojure.core/*名前空間に:ユーザー、交換されることによって:# 'ユーザー/ *

大丈夫コンパイル

(defrecord Size [width height] 
    SizeOps 
    (* [this factor] (Size. (* width factor) (* height factor)))) 

が、私はそれを使用しようとすると、それは知っているだけ*は私のプロトコルの1です:その後

実装

(* (Size. 1 2) 10) 

IllegalArgumentExceptionがメソッドの実装はありません::*プロトコルの: '' user/SizeOpsはクラスのjava.lang.Longに見つかりました

私は完全に私の実装ではコア*機能を指定することでこの問題を回避ハックすることができます

(defrecord Size [width height] 
    SizeOps 
    (* [this factor] (Size. (clojure.core/* width factor) (clojure.core/* height factor)))) 
(* (Size. 1 2) 10) 

#のuser.Size {:幅10、:高さ20}

しかし、私は取得後で(* 3 4)に電話をかけようとすると、同じIllegalArgumentExceptionとなります。私はdefrecordの実装でclojure.core/*という名前のスペースを使用して胃を胃にすることができますが、私はSizeのレコードとLongDoubleのようにいつものように*と呼ぶことができます。

同様のQ & A:

  • 5438379:ちょうど
  • 6492458私の例のようにコアの*(* "!" 3)"!!!"が、不明瞭::などのコア機能を除くPythonのように動作します*オペレータとStringを拡張(ns user (:refer-clojure :exclude [*]))は、 "上書き"警告を回避しますが、その機能を回避します。(
  • 1535235:同じ、multimethodを使用に向けたジェスチャーが、詳細なしで

私は適切なソリューションをdefmultidefmethodまたはdeftype/deriveのような低レベルのディスパッチ機能のどこかにある疑いがあるが、私はのニュアンスを持つスーパー慣れていませんよClojureのruntime polymorphism。そして、私はつもりだ、それぞれが+-*/操作のいくつかのサブセットをサポートするタイプをなどSizePointRectangleCircle、全体のホストを持っているので、私は伝える方法がありますかどうかを知るのが大好きですdefprotocol既存の関数のポリモーフィズムに参加する/構築することは、既存の関数を単純に上書きするのではなく、

答えて

3

このような場合には、それ自体でプロトコルの制限に遭遇すると、その機能の一部についてプロトコルメソッドを呼び出すだけの別の関数を作成するのに役立ちます。

  • をすべてのパスは2つの引数のパスを除いてちょうど:カップルの利点は、私がここで撮影した具体的なアプローチにあります

    (ns example.size 
        (:refer-clojure :exclude [*]) 
        (:require [clojure.core :as clj])) 
    
    (defprotocol SizeOps 
        (times [this factor])) 
    
    (extend-protocol SizeOps 
        Object 
        (times [this factor] (clj/* this factor))) 
    
    (defrecord Size [width height] 
        SizeOps 
        (times [this factor] (->Size (clj/* width factor) (clj/* height factor)))) 
    
    (defn * 
        ([] (clj/*)) 
        ([x] (clj/* x)) 
        ([x y] (times x y)) 
        ([x y & more] (apply clj/* x y more))) 
    

    :通常のdefn Sに与えられる追加機能を使用して行いますアーリーディスパッチを使用する(これは速い)。 2つの引数のパスはプロトコルディスパッチだけを使用します(これは一般的にあなたがしようとしているものと同じくらい速いと思います)

  • すべてのアリティが維持されるので、通常の古い番号の場合はclojure.core/*になります。

必要に応じて自由に最適化してください。最後に

は、実証する:

(ns example.core 
    (:refer-clojure :exclude [*]) 
    (:require [example.size :refer [* ->Size]])) 

(* (->Size 1 2) 10) ;=> #example.size.Size{:width 10, :height 20} 
(* 3 4) ;=> 12 

うまくいけば、十分に人間工学に基づいた、以前に示唆したように。

関連する問題