2

バッグと呼ばれる記録抹消があります。カウントする項目のリストのように動作します。これは、頻度または国勢調査と呼ばれることもあります。次のことができるようになりたい

(def b (bag/create [:k 1 :k2 3])  
(keys bag)
=> (:k :k1)

私は次のことを試しました:

(defrecord MapBag [state]                                                                                                                                         
  Bag                                                                                                                                                             
   (put-n [self item n]                                                                                                                                          
     (let [new-n (+ n (count self item))]                                                                                                                        
          (MapBag. (assoc state item new-n))))                                                                                                                      

  ;... some stuff

  java.util.Map                                                                                                                                                   
    (getKeys [self] (keys state)) ;TODO TEST                                                                                                                      

  Object                                                                                                                                                          
   (toString [self]                                                                                                                                              
     (str ("Bag: " (:state self)))))   

repl でそれを要求しようとすると、次のようになります。

java.lang.ClassFormatError: Duplicate interface name in class file compile__stub/techne/bag/MapBag (bag.clj:12)

何が起こっている?バッグにキー機能を付けるにはどうすればよいですか? また、clojure の keys 関数が最終的にその引数であるマップで getKeys を呼び出すと仮定して、これを正しい方法で行っていますか?

4

2 に答える 2

4

Defrecord は、それが定義するすべてのレコードが ipersistentmap インターフェースに参加することを自動的に確認します。そのため、何もせずにキーを呼び出すことができます

したがって、レコードを定義し、次のようにキーをインスタンス化して呼び出すことができます。

user> (defrecord rec [k1 k2])
user.rec
user> (def a-rec (rec. 1 2))
#'user/a-rec
user> (keys a-rec)
(:k1 :k2)

エラー メッセージは、宣言の 1 つが、defrecord が無料で提供するインターフェイスを複製していることを示しています。本当は両方かもしれないと思います。

単純なバニラ マップを目的に使用できない理由はありますか? clojure では、可能な場合はプレーンなバニラ データ構造を使用したいことがよくあります。

編集: 何らかの理由で ipersistentmap を含めたくない場合は、deftype を調べてください。

于 2010-09-19T03:10:10.263 に答える
3

ロブの答えはもちろん正しいです。OPのコメントに応えてこれを投稿しています。おそらく、必要な機能をdeftype.

Clojure の「デフォルト マップ」の実装を書いたことがあります。これは通常のマップと同じように機能しますが、内部に存在しないキーについて尋ねられたときに固定のデフォルト値を返す点が異なります。コードはこの Gistにあります。

ユースケースに直接合うかどうかはわかりませんが、次のようなことを行うために使用できます

user> (:earth (assoc (DefaultMap. 0 {}) :earth 8000000000))
8000000000
user> (:mars (assoc (DefaultMap. 0 {}) :earth 8000000000))
0

さらに重要なことは、この種のものを で書くことに何が関係しているのかを理解できるはずですdeftype

繰り返しになりますが、これはに基づいているclojure.core/emit-defrecordので、代わりに Clojure のソースのその部分を参照することもできます... 実行する必要のない多くのことを実行しています (マクロ展開を準備するための関数であるため、多くの構文があります-コードを直接使用するには、そこから取り除かなければならない引用などがありますが、可能な限り最高品質の情報源であることは間違いありません。Clojure 1.2.0 リリースのソース内のそのポイントへの直接リンクを次に示します。

アップデート:

もう1つ、重要かもしれないことに気付きました。この種のものを実装するために特別なマップのようなタイプに依存している場合、クライアントはmergeそれを通常のマップに変換し、その過程で「デフォルト」機能 (実際にはその他の特別な機能) を失う可能性があります。あなたのタイプによって維持される「マップのような」イリュージョンが、通常のマップとして使用され、Clojure の標準関数などに渡されるのに十分なほど完全である限り、それを回避する方法はないと思います。

したがって、あるレベルでは、クライアントはおそらく何らかの「魔法」が関与していることを知る必要があります。(:mars {...})( に noを指定:marsして) のようなクエリに対して正しい答えが得られた場合、これを通常のマップにし{...}ないことを覚えておく必要があります ( -ing を逆にするとうまくいきます)。mergemerge

于 2010-09-19T04:02:44.567 に答える