2010-11-26 9 views
2

OK、struct-mapを定義するClojureマクロを記述し、各フィールドの型を指定するよう呼び出し元に通知します。Clojureのマクロ - 引数から構成された名前を持つバインディングを定義します。

シグネチャは次のようになります。マップにある結合*category-meta*を、作成もそれはstruct-mapというカテゴリを作成しますこれは何があるん

(defmodel category :id Integer :name String) 

を、そして{:id Integer :name String}

ここで達成することが私のマクロですこれは:

(defmacro defmodel [name & field-spec] 
    `(let [fields# (take-nth 2 [email protected])] 
     (defstruct ~name fields#) 
     (def *~name-meta* (reduce #(assoc %1 (first %2) (last %2))) (partition 2 [email protected])))) 

しかし、問題は、私は名前が構成されているバインディングを定義することはできません別の名前。基本的に(def *~name-meta* ...)は動作しません。

これをどのように達成できますか?

ありがとうございました。

答えて

3

(問題文からマクロのデバッグバージョンで更新。)

指定されたように、これは動作するはずです:

(defmacro defmodel [name & field-spec] 
    `(do (defstruct ~name [email protected](take-nth 2 field-spec)) 
     (def ~(symbol (str "*" name "-meta*")) 
     (reduce #(assoc %1 (first %2) (last %2)) 
       {} 
       (partition 2 '~field-spec))))) 

メインの質問への答えが所定の位置に~(symbol (str "*" name "-meta*"))を使用することです*~name-meta*~は、構文引用形式の次の式を引用符で囲み、その戻り値を指定されたリスト構造の適切な場所に挿入します。

特に、defstructでは、単一のseq(またはそのようなseqを保持する変数の名前)ではなく別の引数としてキーを指定する必要があり、reduceには明示的な種が必要ですここで働く価値など

ちなみに、Clojure 1.1に固執する必要がある場合を除き、defstructよりも1.2のdefrecordを使用することをお勧めします。後者は1.2では非推奨です。

+0

あなたの明確な答えをありがとう。前にdefrecordを見ましたが、主な欠点は、初期化時にすべてのフィールド値を指定する必要があることです。例えば、(defrecord category [^ String id^String name])、これを '(category。1"スタック ")'のように呼び出す必要があります。私は(category。{:id 1:name "Stack"})、(category。1)のようなことはできません。レコードのサポートのようなものは未だ暫定的で、いつでも変更される可能性があります。私が今までレコードについて知らなかったものを教えてください:) – EnToutCas

+0

私はあなたが望むことを請負業者に書くことを検討します。記録はより速く、プロトコルを実装することができます。もっと強力なレコードがあります。https://github.com/david-mcneil/defrecord2 – nickik

+0

@EnToutCas:暫定的なレコードサポートを呼び出すことはありません。実際には構造マップは廃止されました。交換を提案した。 (これは、工場の機能に関するいくつかの大会の可能性については何も言及していませんが、将来的にはコアライブラリに入り込む可能性があります。)あなたの特定の問題に関しては、私は基本的にnickikと合意しています。 (実際には、ほとんどすべてのレコードのファクトリ関数を書くことが有用であることを知っています。後でそれらを ':import'する必要がない場合は、すべてのレコードをインポートする必要があります)@nickik:リンクありがとう! –

関連する問題